From 516f52c5a416d5b50c231ec3c4ba701c44be7a61 Mon Sep 17 00:00:00 2001 From: Germain Date: Thu, 22 Sep 2022 09:37:54 +0100 Subject: [PATCH] Support to remotely toggle push notifications (#2686) --- spec/integ/matrix-client-methods.spec.js | 1 + spec/test-utils/test-utils.ts | 13 ++++- spec/unit/pusher.spec.ts | 69 ++++++++++++++++++++++++ src/@types/PushRules.ts | 6 ++- src/@types/event.ts | 9 ++++ src/client.ts | 18 ++++++- 6 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 spec/unit/pusher.spec.ts diff --git a/spec/integ/matrix-client-methods.spec.js b/spec/integ/matrix-client-methods.spec.js index 0dd33a02c..85b18f93e 100644 --- a/spec/integ/matrix-client-methods.spec.js +++ b/spec/integ/matrix-client-methods.spec.js @@ -889,6 +889,7 @@ describe("MatrixClient", function() { }; const prom = client.getPushers(); + httpBackend.when("GET", "/_matrix/client/versions").respond(200, {}); httpBackend.when("GET", "/pushers").respond(200, response); await httpBackend.flush(); expect(await prom).toStrictEqual(response); diff --git a/spec/test-utils/test-utils.ts b/spec/test-utils/test-utils.ts index 16d1cb565..0187b12f5 100644 --- a/spec/test-utils/test-utils.ts +++ b/spec/test-utils/test-utils.ts @@ -6,7 +6,7 @@ import '../olm-loader'; import { logger } from '../../src/logger'; import { IContent, IEvent, IUnsigned, MatrixEvent, MatrixEventEvent } from "../../src/models/event"; -import { ClientEvent, EventType, MatrixClient, MsgType } from "../../src"; +import { ClientEvent, EventType, IPusher, MatrixClient, MsgType } from "../../src"; import { SyncState } from "../../src/sync"; import { eventMapperFor } from "../../src/event-mapper"; @@ -371,3 +371,14 @@ export async function awaitDecryption(event: MatrixEvent): Promise } export const emitPromise = (e: EventEmitter, k: string): Promise => new Promise(r => e.once(k, r)); + +export const mkPusher = (extra: Partial = {}): IPusher => ({ + app_display_name: "app", + app_id: "123", + data: {}, + device_display_name: "name", + kind: "http", + lang: "en", + pushkey: "pushpush", + ...extra, +}); diff --git a/spec/unit/pusher.spec.ts b/spec/unit/pusher.spec.ts new file mode 100644 index 000000000..4a27ef55b --- /dev/null +++ b/spec/unit/pusher.spec.ts @@ -0,0 +1,69 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import MockHttpBackend from 'matrix-mock-request'; + +import { IHttpOpts, MatrixClient, PUSHER_ENABLED } from "../../src/matrix"; +import { mkPusher } from '../test-utils/test-utils'; + +const realSetTimeout = setTimeout; +function flushPromises() { + return new Promise(r => { + realSetTimeout(r, 1); + }); +} + +let client: MatrixClient; +let httpBackend: MockHttpBackend; + +describe("Pushers", () => { + beforeEach(() => { + httpBackend = new MockHttpBackend(); + client = new MatrixClient({ + baseUrl: "https://my.home.server", + accessToken: "my.access.token", + request: httpBackend.requestFn as unknown as IHttpOpts["request"], + }); + }); + + describe("supports remotely toggling push notifications", () => { + it("migration support when connecting to a legacy homeserver", async () => { + httpBackend.when("GET", "/_matrix/client/versions").respond(200, { + unstable_features: { + "org.matrix.msc3881": false, + }, + }); + httpBackend.when("GET", "/pushers").respond(200, { + pushers: [ + mkPusher(), + mkPusher({ [PUSHER_ENABLED.name]: true }), + mkPusher({ [PUSHER_ENABLED.name]: false }), + ], + }); + + const promise = client.getPushers(); + + await httpBackend.flushAllExpected(); + await flushPromises(); + + const response = await promise; + + expect(response.pushers[0][PUSHER_ENABLED.name]).toBe(true); + expect(response.pushers[1][PUSHER_ENABLED.name]).toBe(true); + expect(response.pushers[2][PUSHER_ENABLED.name]).toBe(false); + }); + }); +}); diff --git a/src/@types/PushRules.ts b/src/@types/PushRules.ts index 12d1b0d31..7af6d1481 100644 --- a/src/@types/PushRules.ts +++ b/src/@types/PushRules.ts @@ -156,9 +156,13 @@ export interface IPusher { lang: string; profile_tag?: string; pushkey: string; + enabled?: boolean | null | undefined; + "org.matrix.msc3881.enabled"?: boolean | null | undefined; + device_id?: string | null; + "org.matrix.msc3881.device_id"?: string | null; } -export interface IPusherRequest extends IPusher { +export interface IPusherRequest extends Omit { append?: boolean; } diff --git a/src/@types/event.ts b/src/@types/event.ts index dac2770ad..9515283f8 100644 --- a/src/@types/event.ts +++ b/src/@types/event.ts @@ -191,6 +191,15 @@ export const EVENT_VISIBILITY_CHANGE_TYPE = new UnstableValue( "m.visibility", "org.matrix.msc3531.visibility"); +/** + * https://github.com/matrix-org/matrix-doc/pull/3881 + * + * @experimental + */ +export const PUSHER_ENABLED = new UnstableValue( + "enabled", + "org.matrix.msc3881.enabled"); + export interface IEncryptedFile { url: string; mimetype?: string; diff --git a/src/client.ts b/src/client.ts index 8c4df176c..775f96ef1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -159,6 +159,7 @@ import { import { EventType, MsgType, + PUSHER_ENABLED, RelationType, RoomCreateTypeField, RoomType, @@ -8135,8 +8136,21 @@ export class MatrixClient extends TypedEventEmitter { - return this.http.authedRequest(callback, Method.Get, "/pushers"); + public async getPushers(callback?: Callback): Promise<{ pushers: IPusher[] }> { + const response = await this.http.authedRequest(callback, Method.Get, "/pushers"); + + // Migration path for clients that connect to a homeserver that does not support + // MSC3881 yet, see https://github.com/matrix-org/matrix-spec-proposals/blob/kerry/remote-push-toggle/proposals/3881-remote-push-notification-toggling.md#migration + if (!await this.doesServerSupportUnstableFeature("org.matrix.msc3881")) { + response.pushers = response.pushers.map(pusher => { + if (!pusher.hasOwnProperty(PUSHER_ENABLED.name)) { + pusher[PUSHER_ENABLED.name] = true; + } + return pusher; + }); + } + + return response; } /**