Skip to content

Commit

Permalink
feat: automatically join mixed conversations [FS-1897] (#15248)
Browse files Browse the repository at this point in the history
* feat: base for initialisation and finalisation methods

* feat: check if migration time has arrived after mls is supported

* refactor: improve init migration api

* refactor: rename func

* refactor: move initialisation and finalisation to separate modules

* runfix: start migration flow in background after app was initialised

* feat: filter unestablished mls conversations out

* feat: filter out only mls conversations that are unestablished

* refactor: move mls migration logger to separate file

* feat: update conversation locally

* chore: remove migration init code (follow-up pr)

* refactor: resolve core and api client in module

* feat: read feature config from teamstate

* chore: update core

* runfix: check fresh migration config when timer elapses

* chore: update comment

* refactor: use reduce for grouping conversations by protocol

* refactor: simplify removal key check

* feat: update core with new migration config types

* refactor: improve types

* feat: initialise migration of proteus conversations

* feat: create mls group after switching to mixed and add other clients

* feat: periodically check migration config [FS-1893] (#15117)

* feat: base for initialisation and finalisation methods

* feat: check if migration time has arrived after mls is supported

* refactor: improve init migration api

* refactor: rename func

* refactor: move initialisation and finalisation to separate modules

* runfix: start migration flow in background after app was initialised

* feat: filter unestablished mls conversations out

* feat: filter out only mls conversations that are unestablished

* refactor: move mls migration logger to separate file

* feat: update conversation locally

* chore: remove migration init code (follow-up pr)

* refactor: resolve core and api client in module

* feat: read feature config from teamstate

* chore: update core

* runfix: check fresh migration config when timer elapses

* chore: update comment

* refactor: use reduce for grouping conversations by protocol

* refactor: simplify removal key check

* feat: update core with new migration config types

* refactor: improve types

* runfix: send messages with mls if conversation is actually mls (not if group id exists)

* chore: bump core

* refactor: don't replace conversation's reference

* feat: insert system message after conversation protocol update

* refactor: improve reaction to protocol update event

* refactor: move protocol update logic to conversation repository

* feat: save conversation state to db after updating protocol

* feat: update conversation protocol-related fields after protocol was updated

* refactor: move adding users of conversation to separate module

* refactor: move establishing group for mixed conversation to separate module

* chore: update comments

* runfix: don't try to to add users to mls group if mixed conv is empty

* test: adding all conversation members to mls group

* test: try establishing mls group for mixed conversation

* chore: remove comment

* test: conversation repo updateConversationProtocol

* test: initialise migration of proteus conversations

* refactor: CR suggestions

* feat: join unestablished mixed conversations

* test: join unestablished mixed conversations

* feat: periodically check migration config [FS-1893] (#15117)

* feat: base for initialisation and finalisation methods

* feat: check if migration time has arrived after mls is supported

* refactor: improve init migration api

* refactor: rename func

* refactor: move initialisation and finalisation to separate modules

* runfix: start migration flow in background after app was initialised

* feat: filter unestablished mls conversations out

* feat: filter out only mls conversations that are unestablished

* refactor: move mls migration logger to separate file

* feat: update conversation locally

* chore: remove migration init code (follow-up pr)

* refactor: resolve core and api client in module

* feat: read feature config from teamstate

* chore: update core

* runfix: check fresh migration config when timer elapses

* chore: update comment

* refactor: use reduce for grouping conversations by protocol

* refactor: simplify removal key check

* feat: update core with new migration config types

* refactor: improve types

* feat: start migration of proteus conversations [FS-1888] (#15198)

* feat: base for initialisation and finalisation methods

* feat: check if migration time has arrived after mls is supported

* refactor: improve init migration api

* refactor: rename func

* refactor: move initialisation and finalisation to separate modules

* runfix: start migration flow in background after app was initialised

* feat: filter unestablished mls conversations out

* feat: filter out only mls conversations that are unestablished

* refactor: move mls migration logger to separate file

* feat: update conversation locally

* chore: remove migration init code (follow-up pr)

* refactor: resolve core and api client in module

* feat: read feature config from teamstate

* chore: update core

* runfix: check fresh migration config when timer elapses

* chore: update comment

* refactor: use reduce for grouping conversations by protocol

* refactor: simplify removal key check

* feat: update core with new migration config types

* refactor: improve types

* feat: initialise migration of proteus conversations

* feat: create mls group after switching to mixed and add other clients

* runfix: send messages with mls if conversation is actually mls (not if group id exists)

* chore: bump core

* refactor: don't replace conversation's reference

* feat: insert system message after conversation protocol update

* refactor: improve reaction to protocol update event

* refactor: move protocol update logic to conversation repository

* feat: save conversation state to db after updating protocol

* feat: update conversation protocol-related fields after protocol was updated

* refactor: move adding users of conversation to separate module

* refactor: move establishing group for mixed conversation to separate module

* chore: update comments

* runfix: don't try to to add users to mls group if mixed conv is empty

* test: adding all conversation members to mls group

* test: try establishing mls group for mixed conversation

* chore: remove comment

* test: conversation repo updateConversationProtocol

* test: initialise migration of proteus conversations

* refactor: CR suggestions

* chore: improve logs in add users method

* chore: bump core
  • Loading branch information
PatrykBuniX committed Jun 6, 2023
1 parent 78c8657 commit 31b2abb
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/__mocks__/@wireapp/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class Account extends EventEmitter {
conversation: {
send: jest.fn(),
isMLSConversationEstablished: jest.fn(),
joinByExternalCommit: jest.fn(),
addUsersToMLSConversation: jest.fn(),
messageTimer: {
setConversationLevelTimer: jest.fn(),
Expand Down
6 changes: 5 additions & 1 deletion src/script/mls/MLSMigration/MLSMigration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {TeamState} from 'src/script/team/TeamState';
import {TIME_IN_MILLIS} from 'Util/TimeUtil';

import {initialiseMigrationOfProteusConversations} from './initialiseMigration';
import {joinUnestablishedMixedConversations} from './initialiseMigration/joinUnestablishedMixedConversations';
import {mlsMigrationLogger} from './MLSMigrationLogger';

import {isMLSSupportedByEnvironment} from '../isMLSSupportedByEnvironment';
Expand Down Expand Up @@ -173,13 +174,16 @@ const migrateConversationsToMLS = async ({
!conversation.isTeam1to1(),
);

const {proteus: proteusConversations} = groupConversationsByProtocol(regularGroupConversations);
const {proteus: proteusConversations, mixed: mixedConversatons} =
groupConversationsByProtocol(regularGroupConversations);

await initialiseMigrationOfProteusConversations(proteusConversations, {
core,
conversationRepository,
selfUserId,
});

await joinUnestablishedMixedConversations(mixedConversatons, {core});

//TODO: implement logic for init and finalise the migration
};
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ export const addMixedConversationMembersToMLSGroup = async (
qualifiedUsers: usersToAdd,
});

mlsMigrationLogger.info(
`Added ${usersToAdd.length} users to MLS Group for conversation ${mixedConversation.qualifiedId.id}.`,
);
if (otherUsersToAdd.length > 0) {
mlsMigrationLogger.info(
`Added ${otherUsersToAdd.length} users to MLS Group for conversation ${mixedConversation.qualifiedId.id}.`,
);
}

return addUsersResponse;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

export * from './joinUnestablishedMixedConversations';
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {ConversationProtocol, CONVERSATION_TYPE} from '@wireapp/api-client/lib/conversation';
import {container} from 'tsyringe';

import {MixedConversation} from 'src/script/conversation/ConversationSelectors';
import {Conversation} from 'src/script/entity/Conversation';
import {Core} from 'src/script/service/CoreSingleton';
import {createUuid} from 'Util/uuid';

import {joinUnestablishedMixedConversations} from './';

const createMixedConversation = (mockGroupId: string): MixedConversation => {
const conversation = new Conversation(createUuid(), '', ConversationProtocol.MIXED);
conversation.groupId = mockGroupId;
conversation.type(CONVERSATION_TYPE.REGULAR);
return conversation as MixedConversation;
};

const mockCore = container.resolve(Core);

describe('tryEstablishingMLSGroupForMixedConversation', () => {
it('Should join known "mixed" conversations with unestablished MLS groups', async () => {
const mixedConversation1 = createMixedConversation('groupId1');
const mixedConversation2 = createMixedConversation('groupId2');
const mixedConversation3 = createMixedConversation('unestablishedGroup3');
const mixedConversation4 = createMixedConversation('unestablishedGroup4');

jest.spyOn(mockCore.service!.conversation!, 'isMLSConversationEstablished').mockImplementation(groupId => {
return Promise.resolve(!groupId.includes('unestablished'));
});

await joinUnestablishedMixedConversations(
[mixedConversation1, mixedConversation2, mixedConversation3, mixedConversation4],
{
core: mockCore,
},
);

expect(mockCore.service?.conversation?.isMLSConversationEstablished).toHaveBeenCalledTimes(4);
expect(mockCore.service?.conversation?.joinByExternalCommit).toHaveBeenCalledTimes(2);

expect(mockCore.service?.conversation?.joinByExternalCommit).toHaveBeenCalledWith(mixedConversation3.qualifiedId);
expect(mockCore.service?.conversation?.joinByExternalCommit).toHaveBeenCalledWith(mixedConversation4.qualifiedId);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {Account} from '@wireapp/core';

import {MixedConversation} from 'src/script/conversation/ConversationSelectors';

import {mlsMigrationLogger} from '../../MLSMigrationLogger';

interface JoinUnestablishedMixedConversationsParams {
core: Account;
}

export const joinUnestablishedMixedConversations = async (
mixedConversations: MixedConversation[],
{core}: JoinUnestablishedMixedConversationsParams,
) => {
mlsMigrationLogger.info(
`Found ${mixedConversations.length} "mixed" conversations, checking if all of them have established MLS groups...`,
);

for (const mixedConversation of mixedConversations) {
await joinUnestablishedMixedConversation(mixedConversation, {core});
}
};

const joinUnestablishedMixedConversation = async (
mixedConversation: MixedConversation,
{core}: JoinUnestablishedMixedConversationsParams,
) => {
const conversationService = core.service?.conversation;
if (!conversationService) {
throw new Error('ConversationService is not available');
}

const isMLSGroupAlreadyEstablished = await conversationService.isMLSConversationEstablished(
mixedConversation.groupId,
);

if (isMLSGroupAlreadyEstablished) {
return;
}

mlsMigrationLogger.info(
`Found "mixed" conversation without established MLS group: ${mixedConversation.qualifiedId.id}, joining via external commit...`,
);

await conversationService.joinByExternalCommit(mixedConversation.qualifiedId);
};

0 comments on commit 31b2abb

Please sign in to comment.