Skip to content

Commit 6a5eef5

Browse files
author
Germain Souquet
committed
Merge branch 'develop' into gsouquet/threads-rr-poc
2 parents e2fd2de + 3ae974e commit 6a5eef5

21 files changed

+1189
-261
lines changed

.github/workflows/backport.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Backport
2+
on:
3+
pull_request_target:
4+
types:
5+
- closed
6+
- labeled
7+
branches:
8+
- develop
9+
10+
jobs:
11+
backport:
12+
name: Backport
13+
runs-on: ubuntu-latest
14+
# Only react to merged PRs for security reasons.
15+
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
16+
if: >
17+
github.event.pull_request.merged
18+
&& (
19+
github.event.action == 'closed'
20+
|| (
21+
github.event.action == 'labeled'
22+
&& contains(github.event.label.name, 'backport')
23+
)
24+
)
25+
steps:
26+
- uses: tibdex/backport@v2
27+
with:
28+
labels_template: "<%= JSON.stringify(labels) %>"
29+
# We can't use GITHUB_TOKEN here or CI won't run on the new PR
30+
github_token: ${{ secrets.ELEMENT_BOT_TOKEN }}

.github/workflows/jsdoc.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
cp -a "$RUNNER_TEMP/$VERSION" .
4141
4242
# Add the new directory to the index if it isn't there already
43-
if ! grep -q "Version $VERSION" index.html; then
43+
if ! grep -q ">Version $VERSION</a>" index.html; then
4444
perl -i -pe 'BEGIN {$rel=shift} $_ =~ /^<\/ul>/ && print
4545
"<li><a href=\"${rel}/index.html\">Version ${rel}</a></li>\n"' "$VERSION" index.html
4646
fi

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
Changes in [19.3.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.3.0) (2022-08-16)
2+
==================================================================================================
3+
4+
## ✨ Features
5+
* Add txn_id support to sliding sync ([\#2567](https://github.com/matrix-org/matrix-js-sdk/pull/2567)).
6+
* Emit an event when the client receives TURN servers ([\#2529](https://github.com/matrix-org/matrix-js-sdk/pull/2529)).
7+
* Add support for stable prefixes for MSC2285 ([\#2524](https://github.com/matrix-org/matrix-js-sdk/pull/2524)).
8+
* Remove stream-replacement ([\#2551](https://github.com/matrix-org/matrix-js-sdk/pull/2551)).
9+
* Add support for sending user-defined encrypted to-device messages ([\#2528](https://github.com/matrix-org/matrix-js-sdk/pull/2528)).
10+
* Retry to-device messages ([\#2549](https://github.com/matrix-org/matrix-js-sdk/pull/2549)). Fixes vector-im/element-web#12851.
11+
* Sliding sync: add missing filters from latest MSC ([\#2555](https://github.com/matrix-org/matrix-js-sdk/pull/2555)).
12+
* Use stable prefixes for MSC3827 ([\#2537](https://github.com/matrix-org/matrix-js-sdk/pull/2537)).
13+
14+
## 🐛 Bug Fixes
15+
* Fix: Handle parsing of a beacon info event without asset ([\#2591](https://github.com/matrix-org/matrix-js-sdk/pull/2591)). Fixes vector-im/element-web#23078.
16+
* Fix finding event read up to if stable private read receipts is missing ([\#2585](https://github.com/matrix-org/matrix-js-sdk/pull/2585)). Fixes vector-im/element-web#23027.
17+
* Fixed a sliding sync issue where history could be interpreted as live events. ([\#2583](https://github.com/matrix-org/matrix-js-sdk/pull/2583)).
18+
* Don't load the sync accumulator if there's already a sync persist in flight ([\#2569](https://github.com/matrix-org/matrix-js-sdk/pull/2569)).
19+
120
Changes in [19.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.2.0) (2022-08-02)
221
==================================================================================================
322

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "matrix-js-sdk",
3-
"version": "19.2.0",
3+
"version": "19.3.0",
44
"description": "Matrix Client-Server SDK for Javascript",
55
"engines": {
66
"node": ">=12.9.0"

spec/integ/sliding-sync-sdk.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
import { SlidingSyncSdk } from "../../src/sliding-sync-sdk";
2929
import { SyncState } from "../../src/sync";
3030
import { IStoredClientOpts } from "../../src/client";
31+
import { logger } from "../../src/logger";
3132

3233
describe("SlidingSyncSdk", () => {
3334
let client: MatrixClient = null;
@@ -372,6 +373,36 @@ describe("SlidingSyncSdk", () => {
372373
gotRoom.getUnreadNotificationCount(NotificationCountType.Total),
373374
).toEqual(1);
374375
});
376+
377+
// Regression test for a bug which caused the timeline entries to be out-of-order
378+
// when the same room appears twice with different timeline limits. E.g appears in
379+
// the list with timeline_limit:1 then appears again as a room subscription with
380+
// timeline_limit:50
381+
it("can return history with a larger timeline_limit", async () => {
382+
const timeline = data[roomA].timeline;
383+
const oldTimeline = [
384+
mkOwnEvent(EventType.RoomMessage, { body: "old event A" }),
385+
mkOwnEvent(EventType.RoomMessage, { body: "old event B" }),
386+
mkOwnEvent(EventType.RoomMessage, { body: "old event C" }),
387+
...timeline,
388+
];
389+
mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, {
390+
timeline: oldTimeline,
391+
required_state: [],
392+
name: data[roomA].name,
393+
initial: true, // e.g requested via room subscription
394+
});
395+
const gotRoom = client.getRoom(roomA);
396+
expect(gotRoom).toBeDefined();
397+
398+
logger.log("want:", oldTimeline.map((e) => (e.type + " : " + (e.content || {}).body)));
399+
logger.log("got:", gotRoom.getLiveTimeline().getEvents().map(
400+
(e) => (e.getType() + " : " + e.getContent().body)),
401+
);
402+
403+
// we expect the timeline now to be oldTimeline (so the old events are in fact old)
404+
assertTimelineEvents(gotRoom.getLiveTimeline().getEvents(), oldTimeline);
405+
});
375406
});
376407
});
377408
});

spec/integ/sliding-sync.spec.ts

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,225 @@ describe("SlidingSync", () => {
562562
});
563563
});
564564

565+
describe("transaction IDs", () => {
566+
beforeAll(setupClient);
567+
afterAll(teardownClient);
568+
const roomId = "!foo:bar";
569+
570+
let slidingSync: SlidingSync;
571+
572+
// really this applies to them all but it's easier to just test one
573+
it("should resolve modifyRoomSubscriptions after SlidingSync.start() is called", async () => {
574+
const roomSubInfo = {
575+
timeline_limit: 1,
576+
required_state: [
577+
["m.room.name", ""],
578+
],
579+
};
580+
// add the subscription
581+
slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client, 1);
582+
// modification before SlidingSync.start()
583+
const subscribePromise = slidingSync.modifyRoomSubscriptions(new Set([roomId]));
584+
let txnId;
585+
httpBackend.when("POST", syncUrl).check(function(req) {
586+
const body = req.data;
587+
logger.debug("got ", body);
588+
expect(body.room_subscriptions).toBeTruthy();
589+
expect(body.room_subscriptions[roomId]).toEqual(roomSubInfo);
590+
expect(body.txn_id).toBeTruthy();
591+
txnId = body.txn_id;
592+
}).respond(200, function() {
593+
return {
594+
pos: "aaa",
595+
txn_id: txnId,
596+
lists: [],
597+
extensions: {},
598+
rooms: {
599+
[roomId]: {
600+
name: "foo bar",
601+
required_state: [],
602+
timeline: [],
603+
},
604+
},
605+
};
606+
});
607+
slidingSync.start();
608+
await httpBackend.flushAllExpected();
609+
await subscribePromise;
610+
});
611+
it("should resolve setList during a connection", async () => {
612+
const newList = {
613+
ranges: [[0, 20]],
614+
};
615+
const promise = slidingSync.setList(0, newList);
616+
let txnId;
617+
httpBackend.when("POST", syncUrl).check(function(req) {
618+
const body = req.data;
619+
logger.debug("got ", body);
620+
expect(body.room_subscriptions).toBeFalsy();
621+
expect(body.lists[0]).toEqual(newList);
622+
expect(body.txn_id).toBeTruthy();
623+
txnId = body.txn_id;
624+
}).respond(200, function() {
625+
return {
626+
pos: "bbb",
627+
txn_id: txnId,
628+
lists: [{ count: 5 }],
629+
extensions: {},
630+
};
631+
});
632+
await httpBackend.flushAllExpected();
633+
await promise;
634+
expect(txnId).toBeDefined();
635+
});
636+
it("should resolve setListRanges during a connection", async () => {
637+
const promise = slidingSync.setListRanges(0, [[20, 40]]);
638+
let txnId;
639+
httpBackend.when("POST", syncUrl).check(function(req) {
640+
const body = req.data;
641+
logger.debug("got ", body);
642+
expect(body.room_subscriptions).toBeFalsy();
643+
expect(body.lists[0]).toEqual({
644+
ranges: [[20, 40]],
645+
});
646+
expect(body.txn_id).toBeTruthy();
647+
txnId = body.txn_id;
648+
}).respond(200, function() {
649+
return {
650+
pos: "ccc",
651+
txn_id: txnId,
652+
lists: [{ count: 5 }],
653+
extensions: {},
654+
};
655+
});
656+
await httpBackend.flushAllExpected();
657+
await promise;
658+
expect(txnId).toBeDefined();
659+
});
660+
it("should resolve modifyRoomSubscriptionInfo during a connection", async () => {
661+
const promise = slidingSync.modifyRoomSubscriptionInfo({
662+
timeline_limit: 99,
663+
});
664+
let txnId;
665+
httpBackend.when("POST", syncUrl).check(function(req) {
666+
const body = req.data;
667+
logger.debug("got ", body);
668+
expect(body.room_subscriptions).toBeTruthy();
669+
expect(body.room_subscriptions[roomId]).toEqual({
670+
timeline_limit: 99,
671+
});
672+
expect(body.txn_id).toBeTruthy();
673+
txnId = body.txn_id;
674+
}).respond(200, function() {
675+
return {
676+
pos: "ddd",
677+
txn_id: txnId,
678+
extensions: {},
679+
};
680+
});
681+
await httpBackend.flushAllExpected();
682+
await promise;
683+
expect(txnId).toBeDefined();
684+
});
685+
it("should reject earlier pending promises if a later transaction is acknowledged", async () => {
686+
// i.e if we have [A,B,C] and see txn_id=C then A,B should be rejected.
687+
const gotTxnIds = [];
688+
const pushTxn = function(req) {
689+
gotTxnIds.push(req.data.txn_id);
690+
};
691+
const failPromise = slidingSync.setListRanges(0, [[20, 40]]);
692+
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "e" }); // missing txn_id
693+
await httpBackend.flushAllExpected();
694+
const failPromise2 = slidingSync.setListRanges(0, [[60, 70]]);
695+
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "f" }); // missing txn_id
696+
await httpBackend.flushAllExpected();
697+
698+
// attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection
699+
// which is a fail.
700+
expect(failPromise).rejects.toEqual(gotTxnIds[0]);
701+
expect(failPromise2).rejects.toEqual(gotTxnIds[1]);
702+
703+
const okPromise = slidingSync.setListRanges(0, [[0, 20]]);
704+
let txnId;
705+
httpBackend.when("POST", syncUrl).check((req) => {
706+
txnId = req.data.txn_id;
707+
}).respond(200, () => {
708+
// include the txn_id, earlier requests should now be reject()ed.
709+
return {
710+
pos: "g",
711+
txn_id: txnId,
712+
};
713+
});
714+
await httpBackend.flushAllExpected();
715+
await okPromise;
716+
717+
expect(txnId).toBeDefined();
718+
});
719+
it("should not reject later pending promises if an earlier transaction is acknowledged", async () => {
720+
// i.e if we have [A,B,C] and see txn_id=B then C should not be rejected but A should.
721+
const gotTxnIds = [];
722+
const pushTxn = function(req) {
723+
gotTxnIds.push(req.data.txn_id);
724+
};
725+
const A = slidingSync.setListRanges(0, [[20, 40]]);
726+
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "A" });
727+
await httpBackend.flushAllExpected();
728+
const B = slidingSync.setListRanges(0, [[60, 70]]);
729+
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "B" }); // missing txn_id
730+
await httpBackend.flushAllExpected();
731+
732+
// attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection
733+
// which is a fail.
734+
expect(A).rejects.toEqual(gotTxnIds[0]);
735+
736+
const C = slidingSync.setListRanges(0, [[0, 20]]);
737+
let pendingC = true;
738+
C.finally(() => {
739+
pendingC = false;
740+
});
741+
httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, () => {
742+
// include the txn_id for B, so C's promise is outstanding
743+
return {
744+
pos: "C",
745+
txn_id: gotTxnIds[1],
746+
};
747+
});
748+
await httpBackend.flushAllExpected();
749+
// A is rejected, see above
750+
expect(B).resolves.toEqual(gotTxnIds[1]); // B is resolved
751+
expect(pendingC).toBe(true); // C is pending still
752+
});
753+
it("should do nothing for unknown txn_ids", async () => {
754+
const promise = slidingSync.setListRanges(0, [[20, 40]]);
755+
let pending = true;
756+
promise.finally(() => {
757+
pending = false;
758+
});
759+
let txnId;
760+
httpBackend.when("POST", syncUrl).check(function(req) {
761+
const body = req.data;
762+
logger.debug("got ", body);
763+
expect(body.room_subscriptions).toBeFalsy();
764+
expect(body.lists[0]).toEqual({
765+
ranges: [[20, 40]],
766+
});
767+
expect(body.txn_id).toBeTruthy();
768+
txnId = body.txn_id;
769+
}).respond(200, function() {
770+
return {
771+
pos: "ccc",
772+
txn_id: "bogus transaction id",
773+
lists: [{ count: 5 }],
774+
extensions: {},
775+
};
776+
});
777+
await httpBackend.flushAllExpected();
778+
expect(txnId).toBeDefined();
779+
expect(pending).toBe(true);
780+
slidingSync.stop();
781+
});
782+
});
783+
565784
describe("extensions", () => {
566785
beforeAll(setupClient);
567786
afterAll(teardownClient);

spec/unit/autodiscovery.spec.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -153,27 +153,26 @@ describe("AutoDiscovery", function() {
153153
]);
154154
});
155155

156-
it("should return FAIL_PROMPT when .well-known returns not-JSON", function() {
156+
it("should return FAIL_PROMPT when .well-known returns not-JSON", async () => {
157157
const httpBackend = getHttpBackend();
158-
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, "abc");
158+
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, "abc", true);
159+
const expected = {
160+
"m.homeserver": {
161+
state: "FAIL_PROMPT",
162+
error: AutoDiscovery.ERROR_INVALID,
163+
base_url: null,
164+
},
165+
"m.identity_server": {
166+
state: "PROMPT",
167+
error: null,
168+
base_url: null,
169+
},
170+
};
159171
return Promise.all([
160172
httpBackend.flushAllExpected(),
161-
AutoDiscovery.findClientConfig("example.org").then((conf) => {
162-
const expected = {
163-
"m.homeserver": {
164-
state: "FAIL_PROMPT",
165-
error: AutoDiscovery.ERROR_INVALID,
166-
base_url: null,
167-
},
168-
"m.identity_server": {
169-
state: "PROMPT",
170-
error: null,
171-
base_url: null,
172-
},
173-
};
174-
175-
expect(conf).toEqual(expected);
176-
}),
173+
AutoDiscovery.findClientConfig("example.org").then(
174+
expect(expected).toEqual,
175+
),
177176
]);
178177
});
179178

0 commit comments

Comments
 (0)