Skip to content

Commit f2eaa07

Browse files
author
Thomas Belin
authored
refactor(core): Add the qualified id matcher util (#11943)
1 parent 37d5ba2 commit f2eaa07

File tree

56 files changed

+704
-620
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+704
-620
lines changed

src/script/calling/CallingRepository.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export class CallingRepository {
275275
await this.apiClient.conversation.api.postOTRMessage(this.selfClientId, conversationId);
276276
} catch (error) {
277277
const mismatch: ClientMismatch = (error as AxiosError).response!.data;
278-
const localClients = await this.messageRepository.createRecipients(conversationId);
278+
const localClients = await this.messageRepository.createRecipients({domain: null, id: conversationId});
279279

280280
const makeClientList = (recipients: UserClients): ClientListEntry[] =>
281281
Object.entries(recipients).reduce(
@@ -390,7 +390,7 @@ export class CallingRepository {
390390

391391
private storeCall(call: Call): void {
392392
this.callState.activeCalls.push(call);
393-
const conversation = this.conversationState.findConversation(call.conversationId);
393+
const conversation = this.conversationState.findConversation({domain: null, id: call.conversationId});
394394
if (conversation) {
395395
conversation.call(call);
396396
}
@@ -404,7 +404,7 @@ export class CallingRepository {
404404
if (index !== -1) {
405405
this.callState.activeCalls.splice(index, 1);
406406
}
407-
const conversation = this.conversationState.findConversation(call.conversationId);
407+
const conversation = this.conversationState.findConversation({domain: null, id: call.conversationId});
408408
if (conversation) {
409409
conversation.call(null);
410410
}
@@ -508,7 +508,9 @@ export class CallingRepository {
508508
//##############################################################################
509509

510510
private async verificationPromise(conversationId: string, userId: string, isResponse: boolean): Promise<void> {
511-
const recipients = await this.messageRepository.createRecipients(conversationId, false, [userId]);
511+
const recipients = await this.messageRepository.createRecipients({domain: null, id: conversationId}, false, [
512+
userId,
513+
]);
512514
const eventInfoEntity = new EventInfoEntity(undefined, conversationId, {recipients});
513515
eventInfoEntity.setType(GENERIC_MESSAGE_TYPE.CALLING);
514516
const consentType = isResponse
@@ -979,7 +981,7 @@ export class CallingRepository {
979981
messageId: createRandomUuid(),
980982
});
981983
const eventInfoEntity = new EventInfoEntity(genericMessage, conversationId, options);
982-
return this.messageRepository.sendCallingMessage(eventInfoEntity, conversationId);
984+
return this.messageRepository.sendCallingMessage(eventInfoEntity, {domain: null, id: conversationId});
983985
};
984986

985987
private readonly sendSFTRequest = (
@@ -1027,7 +1029,7 @@ export class CallingRepository {
10271029
}
10281030

10291031
if (reason === REASON.NOONE_JOINED || reason === REASON.EVERYONE_LEFT) {
1030-
const conversationEntity = this.conversationState.findConversation(conversationId);
1032+
const conversationEntity = this.conversationState.findConversation({domain: null, id: conversationId});
10311033
const callingEvent = EventBuilder.buildCallingTimeoutEvent(
10321034
reason,
10331035
conversationEntity,
@@ -1094,7 +1096,7 @@ export class CallingRepository {
10941096
shouldRing: number,
10951097
conversationType: CONV_TYPE,
10961098
) => {
1097-
const conversationEntity = this.conversationState.findConversation(conversationId);
1099+
const conversationEntity = this.conversationState.findConversation({domain: null, id: conversationId});
10981100
if (!conversationEntity) {
10991101
return;
11001102
}
@@ -1398,7 +1400,7 @@ export class CallingRepository {
13981400
call: Call,
13991401
customSegmentations: Record<string, any> = {},
14001402
) => {
1401-
const conversationEntity = this.conversationState.findConversation(call.conversationId);
1403+
const conversationEntity = this.conversationState.findConversation({domain: null, id: call.conversationId});
14021404
const participants = conversationEntity.participating_user_ets();
14031405
const selfUserTeamId = call.getSelfParticipant().user.id;
14041406
const guests = participants.filter(user => user.isGuest()).length;

src/script/client/ClientRepository.test.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('ClientRepository', () => {
6262
);
6363

6464
const clientEntities = await testFactory.client_repository.getClientsByUserIds(
65-
[entities.user.john_doe.id],
65+
[entities.user.john_doe.qualified_id],
6666
false,
6767
);
6868
expect(clientEntities[entities.user.john_doe.id].length).toBe(allClients.length);
@@ -238,7 +238,7 @@ describe('ClientRepository', () => {
238238
clientEntity.id = clientId;
239239
testFactory.client_repository['clientState'].currentClient(clientEntity);
240240
testFactory.client_repository.selfUser(new User(userId, null));
241-
const result = testFactory.client_repository['isCurrentClient'](userId, clientId);
241+
const result = testFactory.client_repository['isCurrentClient']({domain: null, id: userId}, clientId);
242242

243243
expect(result).toBeTruthy();
244244
});
@@ -247,7 +247,7 @@ describe('ClientRepository', () => {
247247
const clientEntity = new ClientEntity(false, null);
248248
clientEntity.id = clientId;
249249
testFactory.client_repository['clientState'].currentClient(clientEntity);
250-
const result = testFactory.client_repository['isCurrentClient'](userId, 'ABCDE');
250+
const result = testFactory.client_repository['isCurrentClient']({domain: null, id: userId}, 'ABCDE');
251251

252252
expect(result).toBeFalsy();
253253
});
@@ -256,20 +256,21 @@ describe('ClientRepository', () => {
256256
const clientEntity = new ClientEntity(false, null);
257257
clientEntity.id = clientId;
258258
testFactory.client_repository['clientState'].currentClient(clientEntity);
259-
const result = testFactory.client_repository['isCurrentClient']('ABCDE', clientId);
259+
const result = testFactory.client_repository['isCurrentClient']({domain: null, id: 'ABCDE'}, clientId);
260260

261261
expect(result).toBeFalsy();
262262
});
263263

264264
it('throws an error if current client is not set', () => {
265-
const functionCall = () => testFactory.client_repository['isCurrentClient'](userId, clientId);
265+
const functionCall = () => testFactory.client_repository['isCurrentClient']({domain: null, id: userId}, clientId);
266266

267267
expect(functionCall).toThrowError(ClientError);
268268
});
269269

270270
it('throws an error if client ID is not specified', () => {
271271
testFactory.client_repository['clientState'].currentClient(new ClientEntity(false, null));
272-
const functionCall = () => testFactory.client_repository['isCurrentClient'](userId, undefined);
272+
const functionCall = () =>
273+
testFactory.client_repository['isCurrentClient']({domain: null, id: userId}, undefined);
273274

274275
expect(functionCall).toThrowError(ClientError);
275276
});

src/script/client/ClientRepository.ts

Lines changed: 42 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import type {User} from '../entity/User';
4141
import {ClientError} from '../error/ClientError';
4242
import {ClientRecord} from '../storage';
4343
import {ClientState} from './ClientState';
44+
import {matchQualifiedIds} from 'Util/QualifiedId';
4445

4546
export type QualifiedUserClientEntityMap = {[domain: string]: {[userId: string]: ClientEntity[]}};
4647
export type UserClientEntityMap = {[userId: string]: ClientEntity[]};
@@ -83,8 +84,8 @@ export class ClientRepository {
8384
// Service interactions
8485
//##############################################################################
8586

86-
private deleteClientFromDb(userId: string, clientId: string, domain: string | null): Promise<string> {
87-
return this.clientService.deleteClientFromDb(constructClientPrimaryKey(domain, userId, clientId));
87+
private deleteClientFromDb(userId: QualifiedId, clientId: string): Promise<string> {
88+
return this.clientService.deleteClientFromDb(constructClientPrimaryKey(userId, clientId));
8889
}
8990

9091
/**
@@ -164,28 +165,22 @@ export class ClientRepository {
164165
* @param clientPayload Client data to be stored in database
165166
* @returns Resolves with the record stored in database
166167
*/
167-
saveClientInDb(userId: string, clientPayload: ClientRecord): Promise<ClientRecord> {
168-
const primaryKey = constructClientPrimaryKey(clientPayload.domain, userId, clientPayload.id);
168+
saveClientInDb(userId: QualifiedId, clientPayload: ClientRecord): Promise<ClientRecord> {
169+
const primaryKey = constructClientPrimaryKey(userId, clientPayload.id);
169170
return this.clientService.saveClientInDb(primaryKey, clientPayload);
170171
}
171172

172173
/**
173174
* Updates properties for a client record in database.
174175
*
175176
* @todo Merge "meta" property before updating it, Object.assign(payload.meta, changes.meta)
176-
* @param userId User ID of the client owner
177+
* @param userId Qualified User ID of the client owner
177178
* @param clientId Client ID which needs to get updated
178179
* @param changes New values which should be updated on the client
179-
* @param domain Domain of the remote participant (only available in federation-aware webapps)
180180
* @returns Number of updated records
181181
*/
182-
private updateClientInDb(
183-
userId: string,
184-
clientId: string,
185-
changes: Partial<ClientRecord>,
186-
domain: string | null,
187-
): Promise<number> {
188-
const primaryKey = constructClientPrimaryKey(domain, userId, clientId);
182+
private updateClientInDb(userId: QualifiedId, clientId: string, changes: Partial<ClientRecord>): Promise<number> {
183+
const primaryKey = constructClientPrimaryKey(userId, clientId);
189184
// Preserve primary key on update
190185
changes.meta.primary_key = primaryKey;
191186
return this.clientService.updateClientInDb(primaryKey, changes);
@@ -199,15 +194,10 @@ export class ClientRepository {
199194
* @param isVerified New state to apply
200195
* @returns Resolves when the verification state has been updated
201196
*/
202-
async verifyClient(
203-
userId: string,
204-
clientEntity: ClientEntity,
205-
isVerified: boolean,
206-
domain: string | null,
207-
): Promise<void> {
208-
await this.updateClientInDb(userId, clientEntity.id, {meta: {is_verified: isVerified}}, clientEntity.domain);
197+
async verifyClient(userId: QualifiedId, clientEntity: ClientEntity, isVerified: boolean): Promise<void> {
198+
await this.updateClientInDb(userId, clientEntity.id, {meta: {is_verified: isVerified}});
209199
clientEntity.meta.isVerified(isVerified);
210-
amplify.publish(WebAppEvents.CLIENT.VERIFICATION_STATE_CHANGED, {domain, id: userId}, clientEntity, isVerified);
200+
amplify.publish(WebAppEvents.CLIENT.VERIFICATION_STATE_CHANGED, userId, clientEntity, isVerified);
211201
}
212202

213203
/**
@@ -217,17 +207,13 @@ export class ClientRepository {
217207
* @param clientPayload Client data to be stored in database
218208
* @returns Resolves with the record stored in database
219209
*/
220-
private updateClientSchemaInDb(
221-
userId: string,
222-
clientPayload: PublicClient,
223-
domain: string | null,
224-
): Promise<ClientRecord> {
210+
private updateClientSchemaInDb(userId: QualifiedId, clientPayload: PublicClient): Promise<ClientRecord> {
225211
const clientRecord: ClientRecord = {
226212
...clientPayload,
227-
domain,
213+
domain: userId.domain,
228214
meta: {
229215
is_verified: false,
230-
primary_key: constructClientPrimaryKey(domain, userId, clientPayload.id),
216+
primary_key: constructClientPrimaryKey(userId, clientPayload.id),
231217
},
232218
};
233219
return this.saveClientInDb(userId, clientRecord);
@@ -301,7 +287,7 @@ export class ClientRepository {
301287
async deleteClient(clientId: string, password: string): Promise<ClientEntity[]> {
302288
const selfUser = this.selfUser();
303289
await this.clientService.deleteClient(clientId, password);
304-
await this.deleteClientFromDb(selfUser.id, clientId, selfUser.domain);
290+
await this.deleteClientFromDb(selfUser, clientId);
305291
selfUser.removeClient(clientId);
306292
amplify.publish(WebAppEvents.USER.CLIENT_REMOVED, {domain: selfUser.domain, id: selfUser.id}, clientId);
307293
return this.clientState.clients();
@@ -344,9 +330,9 @@ export class ClientRepository {
344330
* @param clientId ID of client to be deleted
345331
* @returns Resolves when a client and its session have been deleted
346332
*/
347-
async removeClient(userId: string, clientId: string, domain: string | null): Promise<string> {
348-
await this.cryptographyRepository.deleteSession(userId, clientId, domain);
349-
return this.deleteClientFromDb(userId, clientId, domain);
333+
async removeClient(userId: QualifiedId, clientId: string): Promise<string> {
334+
await this.cryptographyRepository.deleteSession(userId, clientId);
335+
return this.deleteClientFromDb(userId, clientId);
350336
}
351337

352338
/**
@@ -372,7 +358,7 @@ export class ClientRepository {
372358
Object.entries(userClientMap).map(async ([userId, clients]) => {
373359
clientEntityMap[domain] ||= {};
374360
clientEntityMap[domain][userId] = updateClients
375-
? await this.updateClientsOfUserById(userId, clients, true, domain)
361+
? await this.updateClientsOfUserById({domain, id: userId}, clients, true)
376362
: // TODO(Federation): Check if `isSelfClient` is needed here
377363
ClientMapper.mapClients(clients, false, domain);
378364
}),
@@ -384,13 +370,13 @@ export class ClientRepository {
384370
return clientEntityMap;
385371
}
386372

387-
async getClientsByUserIds(userIds: string[], updateClients: boolean): Promise<UserClientEntityMap> {
373+
async getClientsByUserIds(userIds: QualifiedId[], updateClients: boolean): Promise<UserClientEntityMap> {
388374
const clientEntityMap: UserClientEntityMap = {};
389375
await Promise.all(
390376
userIds.map(async userId => {
391-
const clients = await this.clientService.getClientsByUserId(userId);
392-
clientEntityMap[userId] = updateClients
393-
? await this.updateClientsOfUserById(userId, clients, true, null)
377+
const clients = await this.clientService.getClientsByUserId(userId.id);
378+
clientEntityMap[userId.id] = updateClients
379+
? await this.updateClientsOfUserById(userId, clients, true)
394380
: // TODO(Federation): Check if `isSelfClient` is needed here
395381
ClientMapper.mapClients(clients, false, null);
396382
}),
@@ -399,14 +385,11 @@ export class ClientRepository {
399385
return clientEntityMap;
400386
}
401387

402-
private async getClientByUserIdFromDb(
403-
requestedUserId: string,
404-
requestedDomain: string | null,
405-
): Promise<ClientRecord[]> {
388+
private async getClientByUserIdFromDb(userQualifiedId: QualifiedId): Promise<ClientRecord[]> {
406389
const clients = await this.clientService.loadAllClientsFromDb();
407390
return clients.filter(client => {
408391
const {userId, domain} = ClientEntity.dismantleUserClientId(client.meta.primary_key);
409-
return userId === requestedUserId && (!domain || domain == requestedDomain);
392+
return matchQualifiedIds({domain, id: userId}, userQualifiedId);
410393
});
411394
}
412395

@@ -416,7 +399,7 @@ export class ClientRepository {
416399
*/
417400
async getClientsForSelf(): Promise<ClientEntity[]> {
418401
this.logger.info('Retrieving all clients of the self user from database');
419-
const clientRecords = await this.getClientByUserIdFromDb(this.selfUser().id, this.selfUser().domain);
402+
const clientRecords = await this.getClientByUserIdFromDb(this.selfUser());
420403
const clientEntities = ClientMapper.mapClients(clientRecords, true, this.selfUser().domain);
421404
clientEntities.forEach(clientEntity => this.selfUser().addClient(clientEntity));
422405
return this.selfUser().devices();
@@ -439,7 +422,7 @@ export class ClientRepository {
439422
*/
440423
async updateClientsForSelf(): Promise<ClientEntity[]> {
441424
const clientsData = await this.clientService.getClients();
442-
return this.updateClientsOfUserById(this.selfUser().id, clientsData, false, this.selfUser().domain);
425+
return this.updateClientsOfUserById(this.selfUser(), clientsData, false);
443426
}
444427

445428
/**
@@ -453,21 +436,20 @@ export class ClientRepository {
453436
* @returns Resolves with the entities once clients have been updated
454437
*/
455438
private updateClientsOfUserById(
456-
userId: string,
439+
userId: QualifiedId,
457440
clientsData: RegisteredClient[] | PublicClient[],
458441
publish: boolean = true,
459-
domain: string | null,
460442
): Promise<ClientEntity[]> {
461443
const clientsFromBackend: {[clientId: string]: RegisteredClient | PublicClient} = {};
462444
const clientsStoredInDb: ClientRecord[] = [];
463-
const isSelfUser = userId === this.selfUser().id;
445+
const isSelfUser = matchQualifiedIds(userId, this.selfUser());
464446

465447
for (const client of clientsData) {
466448
clientsFromBackend[client.id] = client;
467449
}
468450

469451
// Find clients in database
470-
return this.getClientByUserIdFromDb(userId, domain)
452+
return this.getClientByUserIdFromDb(userId)
471453
.then(clientsFromDatabase => {
472454
const promises = [];
473455

@@ -476,13 +458,16 @@ export class ClientRepository {
476458
const backendClient = clientsFromBackend[clientId];
477459

478460
if (backendClient) {
479-
const {client, wasUpdated} = ClientMapper.updateClient(databaseClient, {...backendClient, domain});
461+
const {client, wasUpdated} = ClientMapper.updateClient(databaseClient, {
462+
...backendClient,
463+
domain: userId.domain,
464+
});
480465

481466
delete clientsFromBackend[clientId];
482467

483468
if (this.clientState.currentClient() && this.isCurrentClient(userId, clientId)) {
484469
this.logger.warn(`Removing duplicate self client '${clientId}' locally`);
485-
this.removeClient(userId, clientId, domain);
470+
this.removeClient(userId, clientId);
486471
}
487472

488473
// Locally known client changed on backend
@@ -499,7 +484,7 @@ export class ClientRepository {
499484

500485
// Locally known client deleted on backend
501486
this.logger.warn(`Removing client '${clientId}' of user '${userId}' locally`);
502-
this.removeClient(userId, clientId, domain);
487+
this.removeClient(userId, clientId);
503488
}
504489

505490
for (const clientId in clientsFromBackend) {
@@ -511,18 +496,18 @@ export class ClientRepository {
511496

512497
// Locally unknown client new on backend
513498
this.logger.info(`New client '${clientId}' of user '${userId}' will be stored locally`);
514-
if (this.selfUser().id === userId) {
499+
if (matchQualifiedIds(this.selfUser(), userId)) {
515500
this.onClientAdd({client: clientPayload as RegisteredClient});
516501
}
517-
promises.push(this.updateClientSchemaInDb(userId, clientPayload, domain));
502+
promises.push(this.updateClientSchemaInDb(userId, clientPayload));
518503
}
519504

520505
return Promise.all(promises);
521506
})
522-
.then(newRecords => ClientMapper.mapClients(clientsStoredInDb.concat(newRecords), isSelfUser, domain))
507+
.then(newRecords => ClientMapper.mapClients(clientsStoredInDb.concat(newRecords), isSelfUser, userId.domain))
523508
.then(clientEntities => {
524509
if (publish) {
525-
amplify.publish(WebAppEvents.CLIENT.UPDATE, userId, clientEntities, domain);
510+
amplify.publish(WebAppEvents.CLIENT.UPDATE, userId, clientEntities);
526511
}
527512
return clientEntities;
528513
})
@@ -539,7 +524,7 @@ export class ClientRepository {
539524
* @param clientId ID of client to be checked
540525
* @returns Is the client the current local client
541526
*/
542-
private isCurrentClient(userId: string, clientId: string): boolean {
527+
private isCurrentClient(userId: QualifiedId, clientId: string): boolean {
543528
if (!this.clientState.currentClient()) {
544529
throw new ClientError(ClientError.TYPE.CLIENT_NOT_SET, ClientError.MESSAGE.CLIENT_NOT_SET);
545530
}
@@ -549,7 +534,7 @@ export class ClientRepository {
549534
if (!clientId) {
550535
throw new ClientError(ClientError.TYPE.NO_CLIENT_ID, ClientError.MESSAGE.NO_CLIENT_ID);
551536
}
552-
return userId === this.selfUser().id && clientId === this.clientState.currentClient().id;
537+
return matchQualifiedIds(userId, this.selfUser()) && clientId === this.clientState.currentClient().id;
553538
}
554539

555540
//##############################################################################

0 commit comments

Comments
 (0)