You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-30 04:23:07 +03:00
Include extraParams in all HTTP requests (#4860)
* attaching queryParams from client config in getUrl Signed-off-by: rsb-tbg <69879226+rsb-tbg@users.noreply.github.com> * changed client queryParams to QueryDict for consistency and now merging both sets of params in getUrl if one or both exist Signed-off-by: rsb-tbg <69879226+rsb-tbg@users.noreply.github.com> * added tests Signed-off-by: rsb-tbg <69879226+rsb-tbg@users.noreply.github.com> --------- Signed-off-by: rsb-tbg <69879226+rsb-tbg@users.noreply.github.com>
This commit is contained in:
@ -205,4 +205,109 @@ describe("MatrixClient opts", function () {
|
|||||||
expect(res.event_id).toEqual("foo");
|
expect(res.event_id).toEqual("foo");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("with opts.queryParams", function () {
|
||||||
|
let client: MatrixClient;
|
||||||
|
let httpBackend: HttpBackend;
|
||||||
|
const userId = "@rsb-tbg:localhost";
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
httpBackend = new HttpBackend();
|
||||||
|
client = new MatrixClient({
|
||||||
|
fetchFn: httpBackend.fetchFn as typeof globalThis.fetch,
|
||||||
|
store: new MemoryStore() as IStore,
|
||||||
|
baseUrl: baseUrl,
|
||||||
|
userId: userId,
|
||||||
|
accessToken: accessToken,
|
||||||
|
queryParams: { user_id: userId },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
client.stopClient();
|
||||||
|
httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
return httpBackend.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should include queryParams in matrix server requests", async () => {
|
||||||
|
const eventId = "$test:event";
|
||||||
|
httpBackend
|
||||||
|
.when("PUT", "/txn1")
|
||||||
|
.check((req) => {
|
||||||
|
expect(req.path).toContain(`user_id=${encodeURIComponent(userId)}`);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.respond(200, {
|
||||||
|
event_id: eventId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [res] = await Promise.all([
|
||||||
|
client.sendTextMessage("!foo:bar", "test message", "txn1"),
|
||||||
|
httpBackend.flush("/txn1", 1),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(res.event_id).toEqual(eventId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should include queryParams in sync requests", async () => {
|
||||||
|
httpBackend
|
||||||
|
.when("GET", "/versions")
|
||||||
|
.check((req) => {
|
||||||
|
expect(req.path).toContain(`user_id=${encodeURIComponent(userId)}`);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.respond(200, {});
|
||||||
|
|
||||||
|
httpBackend
|
||||||
|
.when("GET", "/pushrules")
|
||||||
|
.check((req) => {
|
||||||
|
expect(req.path).toContain(`user_id=${encodeURIComponent(userId)}`);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.respond(200, {});
|
||||||
|
|
||||||
|
httpBackend
|
||||||
|
.when("POST", "/filter")
|
||||||
|
.check((req) => {
|
||||||
|
expect(req.path).toContain(`user_id=${encodeURIComponent(userId)}`);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.respond(200, { filter_id: "foo" });
|
||||||
|
|
||||||
|
httpBackend
|
||||||
|
.when("GET", "/sync")
|
||||||
|
.check((req) => {
|
||||||
|
expect(req.path).toContain(`user_id=${encodeURIComponent(userId)}`);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.respond(200, syncData);
|
||||||
|
|
||||||
|
client.startClient();
|
||||||
|
await httpBackend.flush("/versions", 1);
|
||||||
|
await httpBackend.flush("/pushrules", 1);
|
||||||
|
await httpBackend.flush("/filter", 1);
|
||||||
|
await Promise.all([httpBackend.flush("/sync", 1), utils.syncPromise(client)]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should merge queryParams with request-specific params", async () => {
|
||||||
|
const eventId = "$test:event";
|
||||||
|
httpBackend
|
||||||
|
.when("PUT", "/txn1")
|
||||||
|
.check((req) => {
|
||||||
|
// Should contain both global queryParams and request-specific params
|
||||||
|
expect(req.path).toContain(`user_id=${encodeURIComponent(userId)}`);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.respond(200, {
|
||||||
|
event_id: eventId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [res] = await Promise.all([
|
||||||
|
client.sendTextMessage("!foo:bar", "test message", "txn1"),
|
||||||
|
httpBackend.flush("/txn1", 1),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(res.event_id).toEqual(eventId);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -521,6 +521,83 @@ describe("FetchHttpApi", () => {
|
|||||||
describe("when fetch.opts.baseUrl does have a trailing slash", () => {
|
describe("when fetch.opts.baseUrl does have a trailing slash", () => {
|
||||||
runTests(baseUrlWithTrailingSlash);
|
runTests(baseUrlWithTrailingSlash);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("extraParams handling", () => {
|
||||||
|
const makeApiWithExtraParams = (extraParams: QueryDict): FetchHttpApi<any> => {
|
||||||
|
const fetchFn = jest.fn();
|
||||||
|
const emitter = new TypedEventEmitter<HttpApiEvent, HttpApiEventHandlerMap>();
|
||||||
|
return new FetchHttpApi(emitter, { baseUrl: localBaseUrl, prefix, fetchFn, extraParams });
|
||||||
|
};
|
||||||
|
|
||||||
|
const userId = "@rsb-tbg:localhost";
|
||||||
|
const encodedUserId = encodeURIComponent(userId);
|
||||||
|
|
||||||
|
it("should include extraParams in URL when no queryParams provided", () => {
|
||||||
|
const extraParams = { user_id: userId, version: "1.0" };
|
||||||
|
const api = makeApiWithExtraParams(extraParams);
|
||||||
|
|
||||||
|
const result = api.getUrl("/test");
|
||||||
|
expect(result.toString()).toBe(`${localBaseUrl}${prefix}/test?user_id=${encodedUserId}&version=1.0`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should merge extraParams with queryParams", () => {
|
||||||
|
const extraParams = { user_id: userId, version: "1.0" };
|
||||||
|
const api = makeApiWithExtraParams(extraParams);
|
||||||
|
|
||||||
|
const queryParams = { userId: "123", filter: "active" };
|
||||||
|
const result = api.getUrl("/test", queryParams);
|
||||||
|
|
||||||
|
expect(result.searchParams.get("user_id")!).toBe(userId);
|
||||||
|
expect(result.searchParams.get("version")!).toBe("1.0");
|
||||||
|
expect(result.searchParams.get("userId")!).toBe("123");
|
||||||
|
expect(result.searchParams.get("filter")!).toBe("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow queryParams to override extraParams", () => {
|
||||||
|
const extraParams = { user_id: "@default:localhost", version: "1.0" };
|
||||||
|
const api = makeApiWithExtraParams(extraParams);
|
||||||
|
|
||||||
|
const queryParams = { user_id: "@override:localhost", userId: "123" };
|
||||||
|
const result = api.getUrl("/test", queryParams);
|
||||||
|
|
||||||
|
expect(result.searchParams.get("user_id")).toBe("@override:localhost");
|
||||||
|
expect(result.searchParams.get("version")!).toBe("1.0");
|
||||||
|
expect(result.searchParams.get("userId")!).toBe("123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle empty extraParams", () => {
|
||||||
|
const extraParams = {};
|
||||||
|
const api = makeApiWithExtraParams(extraParams);
|
||||||
|
|
||||||
|
const queryParams = { userId: "123" };
|
||||||
|
const result = api.getUrl("/test", queryParams);
|
||||||
|
|
||||||
|
expect(result.searchParams.get("userId")!).toBe("123");
|
||||||
|
expect(result.searchParams.has("user_id")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work when extraParams is undefined", () => {
|
||||||
|
const fetchFn = jest.fn();
|
||||||
|
const emitter = new TypedEventEmitter<HttpApiEvent, HttpApiEventHandlerMap>();
|
||||||
|
const api = new FetchHttpApi(emitter, { baseUrl: localBaseUrl, prefix, fetchFn });
|
||||||
|
|
||||||
|
const queryParams = { userId: "123" };
|
||||||
|
const result = api.getUrl("/test", queryParams);
|
||||||
|
|
||||||
|
expect(result.searchParams.get("userId")!).toBe("123");
|
||||||
|
expect(result.toString()).toBe(`${localBaseUrl}${prefix}/test?userId=123`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work when queryParams is undefined", () => {
|
||||||
|
const extraParams = { user_id: userId, version: "1.0" };
|
||||||
|
const api = makeApiWithExtraParams(extraParams);
|
||||||
|
|
||||||
|
const result = api.getUrl("/test");
|
||||||
|
|
||||||
|
expect(result.searchParams.get("user_id")!).toBe(userId);
|
||||||
|
expect(result.toString()).toBe(`${localBaseUrl}${prefix}/test?user_id=${encodedUserId}&version=1.0`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not log query parameters", async () => {
|
it("should not log query parameters", async () => {
|
||||||
|
@ -360,7 +360,7 @@ export interface ICreateClientOpts {
|
|||||||
* to all requests with this client. Useful for application services which require
|
* to all requests with this client. Useful for application services which require
|
||||||
* `?user_id=`.
|
* `?user_id=`.
|
||||||
*/
|
*/
|
||||||
queryParams?: Record<string, string>;
|
queryParams?: QueryDict;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encryption key used for encrypting sensitive data (such as e2ee keys) in {@link ICreateClientOpts#cryptoStore}.
|
* Encryption key used for encrypting sensitive data (such as e2ee keys) in {@link ICreateClientOpts#cryptoStore}.
|
||||||
|
@ -379,9 +379,12 @@ export class FetchHttpApi<O extends IHttpOpts> {
|
|||||||
? baseUrlWithFallback.slice(0, -1)
|
? baseUrlWithFallback.slice(0, -1)
|
||||||
: baseUrlWithFallback;
|
: baseUrlWithFallback;
|
||||||
const url = new URL(baseUrlWithoutTrailingSlash + (prefix ?? this.opts.prefix) + path);
|
const url = new URL(baseUrlWithoutTrailingSlash + (prefix ?? this.opts.prefix) + path);
|
||||||
if (queryParams) {
|
// If there are any params, encode and append them to the URL.
|
||||||
encodeParams(queryParams, url.searchParams);
|
if (this.opts.extraParams || queryParams) {
|
||||||
|
const mergedParams = { ...this.opts.extraParams, ...queryParams };
|
||||||
|
encodeParams(mergedParams, url.searchParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import { type MatrixError } from "./errors.ts";
|
import { type MatrixError } from "./errors.ts";
|
||||||
import { type Logger } from "../logger.ts";
|
import { type Logger } from "../logger.ts";
|
||||||
|
import { type QueryDict } from "../utils.ts";
|
||||||
|
|
||||||
export type Body = Record<string, any> | BodyInit;
|
export type Body = Record<string, any> | BodyInit;
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ export interface IHttpOpts {
|
|||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
idBaseUrl?: string;
|
idBaseUrl?: string;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
extraParams?: Record<string, string>;
|
extraParams?: QueryDict;
|
||||||
|
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user