Chore: Migrate updateMessages to Typescript (#3715)

This commit is contained in:
Diego Mello 2022-02-16 12:02:17 -03:00 committed by GitHub
parent ed716396d0
commit f51ec9ef0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 198 additions and 184 deletions

View File

@ -54,7 +54,7 @@ export interface ILastMessage {
} }
export interface IMessage { export interface IMessage {
_id?: string; _id: string;
msg?: string; msg?: string;
t?: SubscriptionType; t?: SubscriptionType;
ts: Date; ts: Date;

View File

@ -73,6 +73,7 @@ export interface IThread {
autoTranslate?: boolean; autoTranslate?: boolean;
translations?: any; translations?: any;
e2e?: string; e2e?: string;
subscription: { id: string };
} }
export type TThreadModel = IThread & Model; export type TThreadModel = IThread & Model;

View File

@ -38,7 +38,7 @@ export interface IThreadMessage {
autoTranslate?: boolean; autoTranslate?: boolean;
translations?: ITranslations[]; translations?: ITranslations[];
e2e?: string; e2e?: string;
subscription?: { id: string }; subscription: { id: string };
} }
export type TThreadMessageModel = IThreadMessage & Model; export type TThreadMessageModel = IThreadMessage & Model;

View File

@ -1,9 +1,10 @@
import database from '..'; import database from '..';
import { TAppDatabase } from '../interfaces';
import { SUBSCRIPTIONS_TABLE } from '../model/Subscription'; import { SUBSCRIPTIONS_TABLE } from '../model/Subscription';
const getCollection = db => db.get(SUBSCRIPTIONS_TABLE); const getCollection = (db: TAppDatabase) => db.get(SUBSCRIPTIONS_TABLE);
export const getSubscriptionByRoomId = async rid => { export const getSubscriptionByRoomId = async (rid: string) => {
const db = database.active; const db = database.active;
const subCollection = getCollection(db); const subCollection = getCollection(db);
try { try {

View File

@ -1,174 +0,0 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';
import log from '../../utils/log';
import database from '../database';
import { Encryption } from '../encryption';
import { MESSAGE_TYPE_ANY_LOAD } from '../../constants/messageTypeLoad';
import { generateLoadMoreId } from '../utils';
import protectedFunction from './helpers/protectedFunction';
import buildMessage from './helpers/buildMessage';
export default function updateMessages({ rid, update = [], remove = [], loaderItem }) {
try {
if (!((update && update.length) || (remove && remove.length))) {
return;
}
const db = database.active;
return db.action(async () => {
// Decrypt these messages
update = await Encryption.decryptMessages(update);
const subCollection = db.get('subscriptions');
let sub;
try {
sub = await subCollection.find(rid);
} catch (error) {
sub = { id: rid };
log(new Error('updateMessages: subscription not found'));
}
const messagesIds = [...update.map(m => m._id), ...remove.map(m => m._id)];
const msgCollection = db.get('messages');
const threadCollection = db.get('threads');
const threadMessagesCollection = db.get('thread_messages');
const allMessagesRecords = await msgCollection
.query(Q.where('rid', rid), Q.or(Q.where('id', Q.oneOf(messagesIds)), Q.where('t', Q.oneOf(MESSAGE_TYPE_ANY_LOAD))))
.fetch();
const allThreadsRecords = await threadCollection.query(Q.where('rid', rid), Q.where('id', Q.oneOf(messagesIds))).fetch();
const allThreadMessagesRecords = await threadMessagesCollection
.query(Q.where('subscription_id', rid), Q.where('id', Q.oneOf(messagesIds)))
.fetch();
update = update.map(m => buildMessage(m));
// filter messages
let msgsToCreate = update.filter(i1 => !allMessagesRecords.find(i2 => i1._id === i2.id));
let msgsToUpdate = allMessagesRecords.filter(i1 => update.find(i2 => i1.id === i2._id));
// filter threads
const allThreads = update.filter(m => m.tlm);
let threadsToCreate = allThreads.filter(i1 => !allThreadsRecords.find(i2 => i1._id === i2.id));
let threadsToUpdate = allThreadsRecords.filter(i1 => allThreads.find(i2 => i1.id === i2._id));
// filter thread messages
const allThreadMessages = update.filter(m => m.tmid);
let threadMessagesToCreate = allThreadMessages.filter(i1 => !allThreadMessagesRecords.find(i2 => i1._id === i2.id));
let threadMessagesToUpdate = allThreadMessagesRecords.filter(i1 => allThreadMessages.find(i2 => i1.id === i2._id));
// filter loaders to delete
let loadersToDelete = allMessagesRecords.filter(i1 => update.find(i2 => i1.id === generateLoadMoreId(i2._id)));
// Delete
let msgsToDelete = [];
let threadsToDelete = [];
let threadMessagesToDelete = [];
if (remove && remove.length) {
msgsToDelete = allMessagesRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
msgsToDelete = msgsToDelete.map(m => m.prepareDestroyPermanently());
threadsToDelete = allThreadsRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
threadsToDelete = threadsToDelete.map(t => t.prepareDestroyPermanently());
threadMessagesToDelete = allThreadMessagesRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
threadMessagesToDelete = threadMessagesToDelete.map(tm => tm.prepareDestroyPermanently());
}
// Delete loaders
loadersToDelete = loadersToDelete.map(m => m.prepareDestroyPermanently());
if (loaderItem) {
loadersToDelete.push(loaderItem.prepareDestroyPermanently());
}
// Create
msgsToCreate = msgsToCreate.map(message =>
msgCollection.prepareCreate(
protectedFunction(m => {
m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema);
m.subscription.id = sub.id;
Object.assign(m, message);
})
)
);
threadsToCreate = threadsToCreate.map(thread =>
threadCollection.prepareCreate(
protectedFunction(t => {
t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema);
t.subscription.id = sub.id;
Object.assign(t, thread);
})
)
);
threadMessagesToCreate = threadMessagesToCreate.map(threadMessage =>
threadMessagesCollection.prepareCreate(
protectedFunction(tm => {
tm._raw = sanitizedRaw({ id: threadMessage._id }, threadMessagesCollection.schema);
Object.assign(tm, threadMessage);
tm.subscription.id = sub.id;
tm.rid = threadMessage.tmid;
delete threadMessage.tmid;
})
)
);
// Update
msgsToUpdate = msgsToUpdate.map(message => {
const newMessage = update.find(m => m._id === message.id);
try {
return message.prepareUpdate(
protectedFunction(m => {
Object.assign(m, newMessage);
})
);
} catch {
return null;
}
});
threadsToUpdate = threadsToUpdate.map(thread => {
const newThread = allThreads.find(t => t._id === thread.id);
try {
return thread.prepareUpdate(
protectedFunction(t => {
Object.assign(t, newThread);
})
);
} catch {
return null;
}
});
threadMessagesToUpdate = threadMessagesToUpdate.map(threadMessage => {
const newThreadMessage = allThreadMessages.find(t => t._id === threadMessage.id);
try {
return threadMessage.prepareUpdate(
protectedFunction(tm => {
Object.assign(tm, newThreadMessage);
tm.rid = threadMessage.tmid;
delete threadMessage.tmid;
})
);
} catch {
return null;
}
});
const allRecords = [
...msgsToCreate,
...msgsToUpdate,
...msgsToDelete,
...threadsToCreate,
...threadsToUpdate,
...threadsToDelete,
...threadMessagesToCreate,
...threadMessagesToUpdate,
...threadMessagesToDelete,
...loadersToDelete
];
try {
await db.batch(...allRecords);
} catch (e) {
log(e);
}
return allRecords.length;
});
} catch (e) {
log(e);
}
}

View File

@ -0,0 +1,183 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';
import database from '../database';
import { Encryption } from '../encryption';
import { MESSAGE_TYPE_ANY_LOAD } from '../../constants/messageTypeLoad';
import { generateLoadMoreId } from '../utils';
import protectedFunction from './helpers/protectedFunction';
import buildMessage from './helpers/buildMessage';
import { IMessage, TMessageModel, TThreadMessageModel, TThreadModel } from '../../definitions';
import { getSubscriptionByRoomId } from '../database/services/Subscription';
interface IUpdateMessages {
rid: string;
update: IMessage[];
remove: IMessage[];
loaderItem?: TMessageModel;
}
export default async function updateMessages({
rid,
update = [],
remove = [],
loaderItem
}: IUpdateMessages): Promise<number | void> {
if (!((update && update.length) || (remove && remove.length))) {
return Promise.resolve(0);
}
const sub = await getSubscriptionByRoomId(rid);
if (!sub) {
throw new Error('updateMessages: subscription not found');
}
const db = database.active;
return db.write(async () => {
// Decrypt these messages
update = await Encryption.decryptMessages(update);
const messagesIds: string[] = [...update.map(m => m._id), ...remove.map(m => m._id)];
const msgCollection = db.get('messages');
const threadCollection = db.get('threads');
const threadMessagesCollection = db.get('thread_messages');
const allMessagesRecords = await msgCollection
.query(Q.where('rid', rid), Q.or(Q.where('id', Q.oneOf(messagesIds)), Q.where('t', Q.oneOf(MESSAGE_TYPE_ANY_LOAD))))
.fetch();
const allThreadsRecords = await threadCollection.query(Q.where('rid', rid), Q.where('id', Q.oneOf(messagesIds))).fetch();
const allThreadMessagesRecords = await threadMessagesCollection
.query(Q.where('subscription_id', rid), Q.where('id', Q.oneOf(messagesIds)))
.fetch();
update = update.map(m => buildMessage(m));
// filter loaders to delete
let loadersToDelete: TMessageModel[] = allMessagesRecords.filter(i1 =>
update.find(i2 => i1.id === generateLoadMoreId(i2._id))
);
// Delete
let msgsToDelete: TMessageModel[] = [];
let threadsToDelete: TThreadModel[] = [];
let threadMessagesToDelete: TThreadMessageModel[] = [];
if (remove && remove.length) {
msgsToDelete = allMessagesRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
msgsToDelete = msgsToDelete.map(m => m.prepareDestroyPermanently());
threadsToDelete = allThreadsRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
threadsToDelete = threadsToDelete.map(t => t.prepareDestroyPermanently());
threadMessagesToDelete = allThreadMessagesRecords.filter(i1 => remove.find(i2 => i1.id === i2._id));
threadMessagesToDelete = threadMessagesToDelete.map(tm => tm.prepareDestroyPermanently());
}
// Delete loaders
loadersToDelete = loadersToDelete.map(m => m.prepareDestroyPermanently());
if (loaderItem) {
loadersToDelete.push(loaderItem.prepareDestroyPermanently());
}
// filter messages
const filteredMsgsToCreate = update.filter(i1 => !allMessagesRecords.find(i2 => i1._id === i2.id));
const filteredMsgsToUpdate = allMessagesRecords.filter(i1 => update.find(i2 => i1.id === i2._id));
// filter threads
const allThreads = update.filter(m => m.tlm);
const filteredThreadsToCreate = allThreads.filter(i1 => !allThreadsRecords.find(i2 => i1._id === i2.id));
const filteredThreadsToUpdate = allThreadsRecords.filter(i1 => allThreads.find(i2 => i1.id === i2._id));
// filter thread messages
const allThreadMessages = update.filter(m => m.tmid);
const filteredThreadMessagesToCreate = allThreadMessages.filter(i1 => !allThreadMessagesRecords.find(i2 => i1._id === i2.id));
const filteredThreadMessagesToUpdate = allThreadMessagesRecords.filter(i1 => allThreadMessages.find(i2 => i1.id === i2._id));
// Create
const msgsToCreate = filteredMsgsToCreate.map(message =>
msgCollection.prepareCreate(
protectedFunction((m: TMessageModel) => {
m._raw = sanitizedRaw({ id: message._id }, msgCollection.schema);
m.subscription.id = sub.id;
Object.assign(m, message);
})
)
);
const threadsToCreate = filteredThreadsToCreate.map(thread =>
threadCollection.prepareCreate(
protectedFunction((t: TThreadModel) => {
t._raw = sanitizedRaw({ id: thread._id }, threadCollection.schema);
t.subscription.id = sub.id;
Object.assign(t, thread);
})
)
);
const threadMessagesToCreate = filteredThreadMessagesToCreate.map(threadMessage =>
threadMessagesCollection.prepareCreate(
protectedFunction((tm: TThreadMessageModel) => {
tm._raw = sanitizedRaw({ id: threadMessage._id }, threadMessagesCollection.schema);
Object.assign(tm, threadMessage);
tm.subscription.id = sub.id;
if (threadMessage.tmid) {
tm.rid = threadMessage.tmid;
}
delete threadMessage.tmid;
})
)
);
// Update
const msgsToUpdate = filteredMsgsToUpdate.map(message => {
const newMessage = update.find(m => m._id === message.id);
try {
return message.prepareUpdate(
protectedFunction((m: TMessageModel) => {
Object.assign(m, newMessage);
})
);
} catch {
return null;
}
});
const threadsToUpdate = filteredThreadsToUpdate.map(thread => {
const newThread = allThreads.find(t => t._id === thread.id);
try {
return thread.prepareUpdate(
protectedFunction((t: TThreadModel) => {
Object.assign(t, newThread);
})
);
} catch {
return null;
}
});
const threadMessagesToUpdate = filteredThreadMessagesToUpdate.map(threadMessage => {
const newThreadMessage = allThreadMessages.find(t => t._id === threadMessage.id);
try {
return threadMessage.prepareUpdate(
protectedFunction((tm: TThreadMessageModel) => {
Object.assign(tm, newThreadMessage);
if (threadMessage.tmid) {
tm.rid = threadMessage.tmid;
}
delete threadMessage.tmid;
})
);
} catch {
return null;
}
});
const allRecords = [
...msgsToDelete,
...threadsToDelete,
...threadMessagesToDelete,
...loadersToDelete,
...msgsToCreate,
...msgsToUpdate,
...threadsToCreate,
...threadsToUpdate,
...threadMessagesToCreate,
...threadMessagesToUpdate
];
await db.batch(...allRecords);
return allRecords.length;
});
}

View File

@ -191,6 +191,9 @@ const checkServer = async server => {
await waitFor(element(by.label(label))) await waitFor(element(by.label(label)))
.toBeVisible() .toBeVisible()
.withTimeout(10000); .withTimeout(10000);
await waitFor(element(by.id('sidebar-close-drawer')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('sidebar-close-drawer')).tap(); await element(by.id('sidebar-close-drawer')).tap();
}; };

View File

@ -41,7 +41,7 @@ describe('Deep linking', () => {
}); });
await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again."))) await waitFor(element(by[textMatcher]("You've been logged out by the server. Please log in again.")))
.toExist() .toExist()
.withTimeout(10000); // TODO: we need to improve this message .withTimeout(30000); // TODO: we need to improve this message
}); });
const authAndNavigate = async () => { const authAndNavigate = async () => {
@ -97,7 +97,7 @@ describe('Deep linking', () => {
}); });
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`))) await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist() .toExist()
.withTimeout(10000); .withTimeout(30000);
}); });
it('should navigate to the thread using path', async () => { it('should navigate to the thread using path', async () => {
@ -108,7 +108,7 @@ describe('Deep linking', () => {
}); });
await waitFor(element(by.id(`room-view-title-${threadMessage}`))) await waitFor(element(by.id(`room-view-title-${threadMessage}`)))
.toExist() .toExist()
.withTimeout(10000); .withTimeout(30000);
}); });
it('should navigate to the room using rid', async () => { it('should navigate to the room using rid', async () => {
@ -120,7 +120,7 @@ describe('Deep linking', () => {
}); });
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`))) await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist() .toExist()
.withTimeout(15000); .withTimeout(30000);
await tapBack(); await tapBack();
}); });
}); });
@ -144,7 +144,7 @@ describe('Deep linking', () => {
}); });
await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`))) await waitFor(element(by.id(`room-view-title-${data.groups.private.name}`)))
.toExist() .toExist()
.withTimeout(10000); .withTimeout(30000);
}); });
it('should add a not existing server and fallback to the previous one', async () => { it('should add a not existing server and fallback to the previous one', async () => {
@ -155,7 +155,7 @@ describe('Deep linking', () => {
}); });
await waitFor(element(by.id('rooms-list-view'))) await waitFor(element(by.id('rooms-list-view')))
.toBeVisible() .toBeVisible()
.withTimeout(10000); .withTimeout(30000);
await checkServer(data.server); await checkServer(data.server);
}); });
}); });