Remove watermelondb limitation on updating an already updated model (#7067)
* Remove watermelondb limitation on updating an already updated model * Add logic to handle different prepare states and improve logging * fix tests --------- Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
parent
449c5edac9
commit
980c31f40f
83 changed files with 893 additions and 881 deletions
|
|
@ -39,7 +39,7 @@ export async function storeCategories(serverUrl: string, categories: CategoryWit
|
|||
}
|
||||
|
||||
if (models.length > 0) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'storeCategories');
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
|
@ -103,7 +103,7 @@ export async function addChannelToDefaultCategory(serverUrl: string, channel: Ch
|
|||
const models = await prepareCategoryChannels(operator, categoriesWithChannels);
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'addChannelToDefaultCategory');
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export async function switchToChannel(serverUrl: string, channelId: string, team
|
|||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'switchToChannel');
|
||||
}
|
||||
|
||||
if (isTabletDevice) {
|
||||
|
|
@ -124,7 +124,7 @@ export async function removeCurrentUserFromChannel(serverUrl: string, channelId:
|
|||
await removeChannelFromTeamHistory(operator, teamId, channel.id, false);
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'removeCurrentUserFromChannel');
|
||||
}
|
||||
}
|
||||
return {models};
|
||||
|
|
@ -145,7 +145,7 @@ export async function setChannelDeleteAt(serverUrl: string, channelId: string, d
|
|||
const model = channel.prepareUpdate((c) => {
|
||||
c.deleteAt = deleteAt;
|
||||
});
|
||||
await operator.batchRecords([model]);
|
||||
await operator.batchRecords([model], 'setChannelDeleteAt');
|
||||
} catch (error) {
|
||||
logError('FAILED TO BATCH CHANGES FOR CHANNEL DELETE AT', error);
|
||||
}
|
||||
|
|
@ -179,7 +179,7 @@ export async function markChannelAsViewed(serverUrl: string, channelId: string,
|
|||
});
|
||||
PushNotifications.removeChannelNotifications(serverUrl, channelId);
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([member]);
|
||||
await operator.batchRecords([member], 'markChannelAsViewed');
|
||||
}
|
||||
|
||||
return {member};
|
||||
|
|
@ -206,7 +206,7 @@ export async function markChannelAsUnread(serverUrl: string, channelId: string,
|
|||
m.isUnread = true;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([member]);
|
||||
await operator.batchRecords([member], 'markChannelAsUnread');
|
||||
}
|
||||
|
||||
return {member};
|
||||
|
|
@ -226,7 +226,7 @@ export async function resetMessageCount(serverUrl: string, channelId: string) {
|
|||
member.prepareUpdate((m) => {
|
||||
m.messageCount = 0;
|
||||
});
|
||||
await operator.batchRecords([member]);
|
||||
await operator.batchRecords([member], 'resetMessageCount');
|
||||
|
||||
return member;
|
||||
} catch (error) {
|
||||
|
|
@ -254,7 +254,7 @@ export async function storeMyChannelsForTeam(serverUrl: string, teamId: string,
|
|||
}
|
||||
|
||||
if (flattenedModels.length) {
|
||||
await operator.batchRecords(flattenedModels);
|
||||
await operator.batchRecords(flattenedModels, 'storeMyChannelsForTeam');
|
||||
}
|
||||
|
||||
return {models: flattenedModels};
|
||||
|
|
@ -273,7 +273,7 @@ export async function updateMyChannelFromWebsocket(serverUrl: string, channelMem
|
|||
m.roles = channelMember.roles;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
operator.batchRecords([member]);
|
||||
operator.batchRecords([member], 'updateMyChannelFromWebsocket');
|
||||
}
|
||||
}
|
||||
return {model: member};
|
||||
|
|
@ -293,7 +293,7 @@ export async function updateChannelInfoFromChannel(serverUrl: string, channel: C
|
|||
}],
|
||||
prepareRecordsOnly: true});
|
||||
if (!prepareRecordsOnly) {
|
||||
operator.batchRecords(newInfo);
|
||||
operator.batchRecords(newInfo, 'updateChannelInfoFromChannel');
|
||||
}
|
||||
return {model: newInfo};
|
||||
} catch (error) {
|
||||
|
|
@ -317,7 +317,7 @@ export async function updateLastPostAt(serverUrl: string, channelId: string, las
|
|||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([myChannel]);
|
||||
await operator.batchRecords([myChannel], 'updateLastPostAt');
|
||||
}
|
||||
|
||||
return {member: myChannel};
|
||||
|
|
@ -345,7 +345,7 @@ export async function updateMyChannelLastFetchedAt(serverUrl: string, channelId:
|
|||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([myChannel]);
|
||||
await operator.batchRecords([myChannel], 'updateMyChannelLastFetchedAt');
|
||||
}
|
||||
|
||||
return {member: myChannel};
|
||||
|
|
@ -403,7 +403,7 @@ export async function updateChannelsDisplayName(serverUrl: string, channels: Cha
|
|||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'updateChannelsDisplayName');
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export async function updateDraftFile(serverUrl: string, channelId: string, root
|
|||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft]);
|
||||
await operator.batchRecords([draft], 'updateDraftFile');
|
||||
}
|
||||
|
||||
return {draft};
|
||||
|
|
@ -58,7 +58,7 @@ export async function removeDraftFile(serverUrl: string, channelId: string, root
|
|||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft]);
|
||||
await operator.batchRecords([draft], 'removeDraftFile');
|
||||
}
|
||||
|
||||
return {draft};
|
||||
|
|
@ -99,7 +99,7 @@ export async function updateDraftMessage(serverUrl: string, channelId: string, r
|
|||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft]);
|
||||
await operator.batchRecords([draft], 'updateDraftMessage');
|
||||
}
|
||||
|
||||
return {draft};
|
||||
|
|
@ -129,7 +129,7 @@ export async function addFilesToDraft(serverUrl: string, channelId: string, root
|
|||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([draft]);
|
||||
await operator.batchRecords([draft], 'addFilesToDraft');
|
||||
}
|
||||
|
||||
return {draft};
|
||||
|
|
|
|||
|
|
@ -127,14 +127,14 @@ export async function removePost(serverUrl: string, post: PostModel | Post) {
|
|||
}
|
||||
|
||||
if (removeModels.length) {
|
||||
await operator.batchRecords(removeModels);
|
||||
await operator.batchRecords(removeModels, 'removePost (combined user activity)');
|
||||
}
|
||||
} else {
|
||||
const postModel = await getPostById(database, post.id);
|
||||
if (postModel) {
|
||||
const preparedPost = await prepareDeletePost(postModel);
|
||||
if (preparedPost.length) {
|
||||
await operator.batchRecords(preparedPost);
|
||||
await operator.batchRecords(preparedPost, 'removePost');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ export async function markPostAsDeleted(serverUrl: string, post: Post, prepareRe
|
|||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([dbPost]);
|
||||
await operator.batchRecords([dbPost], 'markPostAsDeleted');
|
||||
}
|
||||
return {model};
|
||||
} catch (error) {
|
||||
|
|
@ -229,7 +229,7 @@ export async function storePostsForChannel(
|
|||
}
|
||||
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'storePostsForChannel');
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export async function removeUserFromTeam(serverUrl: string, teamId: string) {
|
|||
models.push(...system);
|
||||
}
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'removeUserFromTeam');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ export async function addSearchToTeamSearchHistory(serverUrl: string, teamId: st
|
|||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'addSearchToTeamHistory');
|
||||
return {searchModel};
|
||||
} catch (error) {
|
||||
logError('Failed addSearchToTeamSearchHistory', error);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export const switchToGlobalThreads = async (serverUrl: string, teamId?: string,
|
|||
models.push(...history);
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'switchToGlobalThreads');
|
||||
}
|
||||
|
||||
const isTabletDevice = await isTablet();
|
||||
|
|
@ -84,7 +84,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
|
|||
currentChannelId: channel.id,
|
||||
});
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'switchToThread');
|
||||
}
|
||||
} else {
|
||||
const modelPromises: Array<Promise<Model[]>> = [];
|
||||
|
|
@ -97,7 +97,7 @@ export const switchToThread = async (serverUrl: string, rootId: string, isFromNo
|
|||
modelPromises.push(prepareCommonSystemValues(operator, commonValues));
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'switchToThread');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ export async function createThreadFromNewPost(serverUrl: string, post: Post, pre
|
|||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'createThreadFromNewPost');
|
||||
}
|
||||
|
||||
return {models};
|
||||
|
|
@ -257,7 +257,7 @@ export async function processReceivedThreads(serverUrl: string, threads: Thread[
|
|||
}
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'processReceivedThreads');
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
|
|
@ -277,7 +277,7 @@ export async function markTeamThreadsAsRead(serverUrl: string, teamId: string, p
|
|||
record.viewedAt = Date.now();
|
||||
}));
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'markTeamThreadsAsRead');
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
|
|
@ -300,7 +300,7 @@ export async function markThreadAsViewed(serverUrl: string, threadId: string, pr
|
|||
});
|
||||
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([thread]);
|
||||
await operator.batchRecords([thread], 'markThreadAsViewed');
|
||||
}
|
||||
|
||||
return {model: thread};
|
||||
|
|
@ -327,7 +327,7 @@ export async function updateThread(serverUrl: string, threadId: string, updatedT
|
|||
record.unreadReplies = updatedThread.unread_replies ?? record.unreadReplies;
|
||||
});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords([model]);
|
||||
await operator.batchRecords([model], 'updateThread');
|
||||
}
|
||||
return {model};
|
||||
} catch (error) {
|
||||
|
|
@ -341,7 +341,7 @@ export async function updateTeamThreadsSync(serverUrl: string, data: TeamThreads
|
|||
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
||||
const models = await operator.handleTeamThreadsSync({data: [data], prepareRecordsOnly});
|
||||
if (!prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'updateTeamThreadsSync');
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export async function setCurrentUserStatusOffline(serverUrl: string) {
|
|||
}
|
||||
|
||||
user.prepareStatus(General.OFFLINE);
|
||||
await operator.batchRecords([user]);
|
||||
await operator.batchRecords([user], 'setCurrentUserStatusOffline');
|
||||
return null;
|
||||
} catch (error) {
|
||||
logError('Failed setCurrentUserStatusOffline', error);
|
||||
|
|
@ -54,7 +54,7 @@ export async function updateLocalCustomStatus(serverUrl: string, user: UserModel
|
|||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'updateLocalCustomStatus');
|
||||
|
||||
return {};
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ export async function addMembersToChannel(serverUrl: string, channelId: string,
|
|||
}));
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'addMembersToChannel');
|
||||
}
|
||||
return {channelMemberships};
|
||||
} catch (error) {
|
||||
|
|
@ -251,7 +251,7 @@ export async function createChannel(serverUrl: string, displayName: string, purp
|
|||
models.push(...categoriesModels.models);
|
||||
}
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'createChannel');
|
||||
}
|
||||
fetchChannelStats(serverUrl, channelData.id, false);
|
||||
EphemeralStore.creatingChannel = false;
|
||||
|
|
@ -296,7 +296,7 @@ export async function patchChannel(serverUrl: string, channelPatch: Partial<Chan
|
|||
models.push(channel);
|
||||
}
|
||||
if (models?.length) {
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'patchChannel');
|
||||
}
|
||||
return {channel: channelData};
|
||||
} catch (error) {
|
||||
|
|
@ -343,7 +343,7 @@ export async function leaveChannel(serverUrl: string, channelId: string) {
|
|||
models.push(...removeUserModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'leaveChannel');
|
||||
|
||||
if (isTabletDevice) {
|
||||
switchToLastChannel(serverUrl);
|
||||
|
|
@ -393,7 +393,7 @@ export async function fetchChannelCreator(serverUrl: string, channelId: string,
|
|||
}));
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'fetchChannelCreator');
|
||||
}
|
||||
|
||||
return {user};
|
||||
|
|
@ -484,7 +484,7 @@ export async function fetchMyChannelsForTeam(serverUrl: string, teamId: string,
|
|||
const {models: catModels} = await storeCategories(serverUrl, categories, true, true); // Re-sync
|
||||
const models = (chModels || []).concat(catModels || []);
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchMyChannelsForTeam');
|
||||
}
|
||||
setTeamLoading(serverUrl, false);
|
||||
}
|
||||
|
|
@ -609,7 +609,7 @@ export async function fetchMissingDirectChannelsInfo(serverUrl: string, directCh
|
|||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'fetchMissingDirectChannelInfo');
|
||||
}
|
||||
|
||||
return {directChannels: updatedChannelsArray, users};
|
||||
|
|
@ -687,7 +687,7 @@ export async function joinChannel(serverUrl: string, teamId: string, channelId?:
|
|||
}
|
||||
if (flattenedModels?.length > 0) {
|
||||
try {
|
||||
await operator.batchRecords(flattenedModels);
|
||||
await operator.batchRecords(flattenedModels, 'joinChannel');
|
||||
} catch {
|
||||
logError('FAILED TO BATCH CHANNELS');
|
||||
}
|
||||
|
|
@ -913,7 +913,7 @@ export async function createDirectChannel(serverUrl: string, userId: string, dis
|
|||
models.push(...userModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'createDirectChannel');
|
||||
EphemeralStore.creatingDMorGMTeammates = [];
|
||||
fetchRolesIfNeeded(serverUrl, member.roles.split(' '));
|
||||
return {data: created};
|
||||
|
|
@ -1047,7 +1047,7 @@ export async function createGroupChannel(serverUrl: string, userIds: string[]) {
|
|||
}
|
||||
|
||||
models.push(...userModels);
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'createGroupChannel');
|
||||
}
|
||||
}
|
||||
EphemeralStore.creatingDMorGMTeammates = [];
|
||||
|
|
@ -1392,7 +1392,7 @@ export const convertChannelToPrivate = async (serverUrl: string, channelId: stri
|
|||
channel.prepareUpdate((c) => {
|
||||
c.type = General.PRIVATE_CHANNEL;
|
||||
});
|
||||
await operator.batchRecords([channel]);
|
||||
await operator.batchRecords([channel], 'convertChannelToPrivate');
|
||||
}
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
|
|||
// clear lastUnreadChannelId
|
||||
const removeLastUnreadChannelId = await prepareCommonSystemValues(operator, {lastUnreadChannelId: ''});
|
||||
if (removeLastUnreadChannelId) {
|
||||
await operator.batchRecords(removeLastUnreadChannelId);
|
||||
await operator.batchRecords(removeLastUnreadChannelId, 'appEntry - removeLastUnreadChannelId');
|
||||
}
|
||||
|
||||
const {database} = operator;
|
||||
|
|
@ -58,14 +58,14 @@ export async function appEntry(serverUrl: string, since = 0, isUpgrade = false)
|
|||
currentChannelId: isTabletDevice ? initialChannelId : undefined,
|
||||
});
|
||||
if (me?.length) {
|
||||
await operator.batchRecords(me);
|
||||
await operator.batchRecords(me, 'appEntry - upgrade store me');
|
||||
}
|
||||
}
|
||||
|
||||
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeamId, currentChannelId, initialTeamId, initialChannelId);
|
||||
|
||||
const dt = Date.now();
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'appEntry');
|
||||
logInfo('ENTRY MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ const graphQLSyncAllChannelMembers = async (serverUrl: string) => {
|
|||
const modelPromises = await prepareMyChannelsForTeam(operator, '', channels, memberships, undefined, true);
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'graphQLSyncAllChannelMembers');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ export async function deferredAppEntryGraphQLActions(
|
|||
modelPromises.push(operator.handleRole({roles, prepareRecordsOnly: true}));
|
||||
}
|
||||
const models = (await Promise.all(modelPromises)).flat();
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'deferredAppEntryActions');
|
||||
|
||||
setTimeout(() => {
|
||||
if (result.chData?.channels?.length && result.chData.memberships?.length) {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export async function loginEntry({serverUrl, user, deviceToken}: AfterLoginArgs)
|
|||
setCurrentTeamAndChannelId(operator, initialTeamId, '');
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'loginEntry');
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
const config = clData.config || {} as ClientConfig;
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ export async function pushNotificationEntry(serverUrl: string, notification: Not
|
|||
await NavigationStore.waitUntilScreenHasLoaded(Screens.THREAD);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'pushNotificationEntry');
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
const {id: currentUserId, locale: currentUserLocale} = (await getCurrentUser(operator.database))!;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import {DOWNLOAD_TIMEOUT} from '@constants/network';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
import {logDebug} from '@utils/log';
|
||||
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
|
||||
|
|
@ -32,10 +33,11 @@ export const uploadFile = (
|
|||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)};
|
||||
} catch (error) {
|
||||
logDebug('uploadFile', error);
|
||||
return {error: error as ClientError};
|
||||
}
|
||||
return {cancel: client.uploadPostAttachment(file, channelId, onProgress, onComplete, onError, skipBytes)};
|
||||
};
|
||||
|
||||
export const fetchPublicLink = async (serverUrl: string, fileId: string) => {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export const fetchGroupsForChannel = async (serverUrl: string, channelId: string
|
|||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupChannels]);
|
||||
await operator.batchRecords([...groups, ...groupChannels], 'fetchGroupsForChannel');
|
||||
}
|
||||
|
||||
return {groups, groupChannels};
|
||||
|
|
@ -110,7 +110,7 @@ export const fetchGroupsForTeam = async (serverUrl: string, teamId: string, fetc
|
|||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupTeams]);
|
||||
await operator.batchRecords([...groups, ...groupTeams], 'fetchGroupsForTeam');
|
||||
}
|
||||
|
||||
return {groups, groupTeams};
|
||||
|
|
@ -136,7 +136,7 @@ export const fetchGroupsForMember = async (serverUrl: string, userId: string, fe
|
|||
]);
|
||||
|
||||
if (!fetchOnly) {
|
||||
await operator.batchRecords([...groups, ...groupMemberships]);
|
||||
await operator.batchRecords([...groups, ...groupMemberships], 'fetchGroupsForMember');
|
||||
}
|
||||
|
||||
return {groups, groupMemberships};
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
|||
initialPostModels.push(...reactionModels);
|
||||
}
|
||||
|
||||
await operator.batchRecords(initialPostModels);
|
||||
await operator.batchRecords(initialPostModels, 'createPost - initial');
|
||||
|
||||
const isCRTEnabled = await getIsCRTEnabled(database);
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
|||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'createPost - failure');
|
||||
}
|
||||
|
||||
return {data: true};
|
||||
|
|
@ -192,7 +192,7 @@ export async function createPost(serverUrl: string, post: Partial<Post>, files:
|
|||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'createPost - success');
|
||||
|
||||
newPost = created;
|
||||
|
||||
|
|
@ -237,7 +237,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
|||
p.props = newPost.props;
|
||||
p.updateAt = timestamp;
|
||||
});
|
||||
await operator.batchRecords([post]);
|
||||
await operator.batchRecords([post], 'retryFailedPost - first update');
|
||||
|
||||
const created = await client.createPost(newPost);
|
||||
const models = await operator.handlePosts({
|
||||
|
|
@ -253,7 +253,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
|||
models.push(member);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'retryFailedPost - success update');
|
||||
} catch (error) {
|
||||
if (isServerError(error) && (
|
||||
error.server_error_id === ServerErrors.DELETED_ROOT_POST_ERROR ||
|
||||
|
|
@ -268,7 +268,7 @@ export const retryFailedPost = async (serverUrl: string, post: PostModel) => {
|
|||
failed: true,
|
||||
};
|
||||
});
|
||||
await operator.batchRecords([post]);
|
||||
await operator.batchRecords([post], 'retryFailedPost - error update');
|
||||
}
|
||||
|
||||
return {error};
|
||||
|
|
@ -375,7 +375,7 @@ export async function fetchPosts(serverUrl: string, channelId: string, page = 0,
|
|||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchPosts');
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
|
@ -434,7 +434,7 @@ export async function fetchPostsBefore(serverUrl: string, channelId: string, pos
|
|||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchPostsBefore');
|
||||
} catch (error) {
|
||||
logError('FETCH POSTS BEFORE ERROR', error);
|
||||
}
|
||||
|
|
@ -489,7 +489,7 @@ export async function fetchPostsSince(serverUrl: string, channelId: string, sinc
|
|||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchPostsSince');
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
|
@ -621,7 +621,7 @@ export async function fetchPostThread(serverUrl: string, postId: string, options
|
|||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchPostThread');
|
||||
}
|
||||
setFetchingThreadState(postId, false);
|
||||
return {posts: extractRecordsForTable<PostModel>(posts, MM_TABLES.SERVER.POST)};
|
||||
|
|
@ -697,7 +697,7 @@ export async function fetchPostsAround(serverUrl: string, channelId: string, pos
|
|||
models.push(...threadModels);
|
||||
}
|
||||
}
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchPostsAround');
|
||||
}
|
||||
|
||||
return {posts: extractRecordsForTable<PostModel>(posts, MM_TABLES.SERVER.POST)};
|
||||
|
|
@ -751,7 +751,7 @@ export async function fetchMissingChannelsFromPosts(serverUrl: string, posts: Po
|
|||
return mdls;
|
||||
});
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchMissingChannelsFromPosts');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -809,7 +809,7 @@ export async function fetchPostById(serverUrl: string, postId: string, fetchOnly
|
|||
}
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchPostById');
|
||||
}
|
||||
|
||||
return {post};
|
||||
|
|
@ -1036,7 +1036,7 @@ export async function fetchSavedPosts(serverUrl: string, teamId?: string, channe
|
|||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchSavedPosts');
|
||||
|
||||
return {
|
||||
order,
|
||||
|
|
@ -1118,7 +1118,7 @@ export async function fetchPinnedPosts(serverUrl: string, channelId: string) {
|
|||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchPinnedPosts');
|
||||
|
||||
return {
|
||||
order,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export async function addReaction(serverUrl: string, postId: string, emojiName:
|
|||
models.push(...recent);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'addReaction');
|
||||
|
||||
return {reaction};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export async function retryInitialTeamAndChannel(serverUrl: string) {
|
|||
),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'retryInitialTeamAndChannel');
|
||||
|
||||
const directChannels = chData!.channels!.filter(isDMorGM);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
|
|
@ -196,7 +196,7 @@ export async function retryInitialChannel(serverUrl: string, teamId: string) {
|
|||
prepareCommonSystemValues(operator, {currentChannelId: initialChannel?.id}),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'retryInitialChannel');
|
||||
|
||||
const directChannels = chData!.channels!.filter(isDMorGM);
|
||||
const channelsToFetchProfiles = new Set<Channel>(directChannels);
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ export const searchPosts = async (serverUrl: string, teamId: string, params: Pos
|
|||
return mdls;
|
||||
});
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'searchPosts');
|
||||
return {
|
||||
order,
|
||||
posts: postsArray,
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export async function addUserToTeam(serverUrl: string, teamId: string, userId: s
|
|||
prepareCategoriesAndCategoriesChannels(operator, categories || [], true),
|
||||
])).flat();
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'addUserToTeam');
|
||||
setTeamLoading(serverUrl, false);
|
||||
loadEventSent = false;
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ export async function fetchMyTeams(serverUrl: string, fetchOnly = false): Promis
|
|||
const models = await Promise.all(modelPromises);
|
||||
const flattenedModels = models.flat();
|
||||
if (flattenedModels.length > 0) {
|
||||
await operator.batchRecords(flattenedModels);
|
||||
await operator.batchRecords(flattenedModels, 'fetchMyTeams');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -233,7 +233,7 @@ export async function fetchMyTeam(serverUrl: string, teamId: string, fetchOnly =
|
|||
const models = await Promise.all(modelPromises);
|
||||
const flattenedModels = models.flat();
|
||||
if (flattenedModels?.length > 0) {
|
||||
await operator.batchRecords(flattenedModels);
|
||||
await operator.batchRecords(flattenedModels, 'fetchMyTeam');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -362,7 +362,7 @@ export async function fetchTeamByName(serverUrl: string, teamName: string, fetch
|
|||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (operator) {
|
||||
const models = await operator.handleTeam({teams: [team], prepareRecordsOnly: true});
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'fetchTeamByName');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -441,7 +441,7 @@ export async function handleTeamChange(serverUrl: string, teamId: string) {
|
|||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'handleTeamChange');
|
||||
}
|
||||
DeviceEventEmitter.emit(Events.TEAM_SWITCH, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export async function updateTermsOfServiceStatus(serverUrl: string, id: string,
|
|||
u.termsOfServiceId = '';
|
||||
}
|
||||
});
|
||||
operator.batchRecords([currentUser]);
|
||||
operator.batchRecords([currentUser], 'updateTermsOfServiceStatus');
|
||||
}
|
||||
return {resp};
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ export const syncTeamThreads = async (serverUrl: string, teamId: string, prepare
|
|||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
try {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'syncTeamThreads');
|
||||
} catch (err) {
|
||||
if (__DEV__) {
|
||||
throw err;
|
||||
|
|
@ -460,7 +460,7 @@ export const loadEarlierThreads = async (serverUrl: string, teamId: string, last
|
|||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
try {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'loadEarlierThreads');
|
||||
} catch (err) {
|
||||
if (__DEV__) {
|
||||
throw err;
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export async function fetchProfilesInChannel(serverUrl: string, channelId: strin
|
|||
modelPromises.push(prepare);
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesInChannel');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +178,7 @@ export async function fetchProfilesInGroupChannels(serverUrl: string, groupChann
|
|||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesInGroupChannels');
|
||||
}
|
||||
|
||||
return {data};
|
||||
|
|
@ -230,7 +230,7 @@ export async function fetchProfilesPerChannels(serverUrl: string, channelIds: st
|
|||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'fetchProfilesPerChannels');
|
||||
}
|
||||
|
||||
return {data};
|
||||
|
|
@ -366,7 +366,7 @@ export async function fetchStatusByIds(serverUrl: string, userIds: string[], fet
|
|||
user.prepareStatus(status?.status || General.OFFLINE);
|
||||
}
|
||||
|
||||
await operator.batchRecords(users);
|
||||
await operator.batchRecords(users, 'fetchStatusByIds');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -641,7 +641,7 @@ export async function updateAllUsersSince(serverUrl: string, since: number, fetc
|
|||
modelsToBatch.push(...models);
|
||||
}
|
||||
|
||||
await operator.batchRecords(modelsToBatch);
|
||||
await operator.batchRecords(modelsToBatch, 'updateAllUsersSince');
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
|
|
@ -677,7 +677,7 @@ export async function updateUsersNoLongerVisible(serverUrl: string, prepareRecor
|
|||
}
|
||||
}
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
serverDatabase.operator.batchRecords(models);
|
||||
serverDatabase.operator.batchRecords(models, 'updateUsersNoLongerVisible');
|
||||
}
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientError);
|
||||
|
|
@ -917,7 +917,7 @@ export const fetchTeamAndChannelMembership = async (serverUrl: string, userId: s
|
|||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'fetchTeamAndChannelMembership');
|
||||
return {error: undefined};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export async function handleCategoryOrderUpdatedEvent(serverUrl: string, msg: We
|
|||
c.sortOrder = order.findIndex(findOrder);
|
||||
});
|
||||
});
|
||||
await operator.batchRecords(categories);
|
||||
await operator.batchRecords(categories, 'handleCategoryOrderUpdatedEvent');
|
||||
}
|
||||
} catch (e) {
|
||||
logError('Category WS: handleCategoryOrderUpdatedEvent', e, msg);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export async function handleChannelCreatedEvent(serverUrl: string, msg: any) {
|
|||
}
|
||||
}
|
||||
}
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'handleChannelCreatedEvent');
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ export async function handleChannelUpdatedEvent(serverUrl: string, msg: any) {
|
|||
if (infoModel.model) {
|
||||
models.push(...infoModel.model);
|
||||
}
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'handleChannelUpdatedEvent');
|
||||
} catch {
|
||||
// Do nothing
|
||||
}
|
||||
|
|
@ -165,7 +165,7 @@ export async function handleChannelMemberUpdatedEvent(serverUrl: string, msg: an
|
|||
if (rolesRequest.roles?.length) {
|
||||
models.push(...await operator.handleRole({roles: rolesRequest.roles, prepareRecordsOnly: true}));
|
||||
}
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'handleChannelMemberUpdatedEvent');
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
|
@ -235,7 +235,7 @@ export async function handleDirectAddedEvent(serverUrl: string, msg: WebSocketMe
|
|||
models.push(...userModels);
|
||||
}
|
||||
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'handleDirectAddedEvent');
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
|
@ -267,7 +267,7 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any)
|
|||
const prepareModels = await Promise.all(prepare);
|
||||
const flattenedModels = prepareModels.flat();
|
||||
if (flattenedModels?.length > 0) {
|
||||
await operator.batchRecords(flattenedModels);
|
||||
await operator.batchRecords(flattenedModels, 'handleUserAddedToChannelEvent - prepareMyChannelsForTeam');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +308,7 @@ export async function handleUserAddedToChannelEvent(serverUrl: string, msg: any)
|
|||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'handleUserAddedToChannelEvent');
|
||||
}
|
||||
|
||||
await fetchChannelStats(serverUrl, channelId, false);
|
||||
|
|
@ -356,7 +356,7 @@ export async function handleUserRemovedFromChannelEvent(serverUrl: string, msg:
|
|||
}
|
||||
}
|
||||
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'handleUserRemovedFromChannelEvent');
|
||||
} catch (error) {
|
||||
logDebug('cannot handle user removed from channel websocket event', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ async function doReconnect(serverUrl: string) {
|
|||
await handleEntryAfterLoadNavigation(serverUrl, teamData.memberships || [], chData?.memberships || [], currentTeam?.id || '', currentChannel?.id || '', initialTeamId, initialChannelId);
|
||||
|
||||
const dt = Date.now();
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'doReconnect');
|
||||
logInfo('WEBSOCKET RECONNECT MODELS BATCHING TOOK', `${Date.now() - dt}ms`);
|
||||
setTeamLoading(serverUrl, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ export async function handleNewPostEvent(serverUrl: string, msg: WebSocketMessag
|
|||
|
||||
models.push(...postModels);
|
||||
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'handleNewPostEvent');
|
||||
}
|
||||
|
||||
export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage) {
|
||||
|
|
@ -220,7 +220,7 @@ export async function handlePostEdited(serverUrl: string, msg: WebSocketMessage)
|
|||
});
|
||||
models.push(...postModels);
|
||||
|
||||
operator.batchRecords(models);
|
||||
operator.batchRecords(models, 'handlePostEdited');
|
||||
}
|
||||
|
||||
export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage) {
|
||||
|
|
@ -263,7 +263,7 @@ export async function handlePostDeleted(serverUrl: string, msg: WebSocketMessage
|
|||
}
|
||||
|
||||
if (models.length) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'handlePostDeleted');
|
||||
}
|
||||
} catch {
|
||||
// Do nothing
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export async function handleUserRoleUpdatedEvent(serverUrl: string, msg: WebSock
|
|||
models.push(user);
|
||||
}
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'handleUserRoleUpdatedEvent');
|
||||
}
|
||||
|
||||
export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: WebSocketMessage): Promise<void> {
|
||||
|
|
@ -118,7 +118,7 @@ export async function handleTeamMemberRoleUpdatedEvent(serverUrl: string, msg: W
|
|||
});
|
||||
models.push(...teamMembership);
|
||||
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'handleTeamMemberRoleUpdatedEvent');
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,5 +162,5 @@ const fetchAndStoreJoinedTeamInfo = async (serverUrl: string, operator: ServerDa
|
|||
}
|
||||
|
||||
const models = await Promise.all(modelPromises);
|
||||
await operator.batchRecords(models.flat());
|
||||
await operator.batchRecords(models.flat(), 'fetchAndStoreJoinedTeamInfo');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export async function handleUserUpdatedEvent(serverUrl: string, msg: WebSocketMe
|
|||
modelsToBatch.push(...userModel);
|
||||
|
||||
try {
|
||||
await operator.batchRecords(modelsToBatch);
|
||||
await operator.batchRecords(modelsToBatch, 'handleUserUpdatedEvent');
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
import {buildQueryString} from '@utils/helpers';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientAppsMix {
|
||||
executeAppCall: <Res = unknown>(call: AppCallRequest, trackAsSubmit: boolean) => Promise<AppCallResponse<Res>>;
|
||||
getAppsBindings: (userID: string, channelID: string, teamID: string) => Promise<AppBinding[]>;
|
||||
}
|
||||
|
||||
const ClientApps = (superclass: any) => class extends superclass {
|
||||
executeAppCall = async <Res = unknown>(call: AppCallRequest, trackAsSubmit: boolean): Promise<AppCallResponse<Res>> => {
|
||||
const ClientApps = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
executeAppCall = async (call: AppCallRequest, trackAsSubmit: boolean) => {
|
||||
const callCopy = {
|
||||
...call,
|
||||
context: {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export default class ClientBase {
|
|||
requestHeaders: {[x: string]: string} = {};
|
||||
serverVersion = '';
|
||||
urlVersion = '/api/v4';
|
||||
enableLogging = false;
|
||||
|
||||
constructor(apiClient: APIClientInterface, serverUrl: string, bearerToken?: string, csrfToken?: string) {
|
||||
this.apiClient = apiClient;
|
||||
|
|
@ -45,6 +46,10 @@ export default class ClientBase {
|
|||
}
|
||||
}
|
||||
|
||||
getBaseRoute() {
|
||||
return this.apiClient.baseUrl || '';
|
||||
}
|
||||
|
||||
getAbsoluteUrl(baseUrl?: string) {
|
||||
if (typeof baseUrl !== 'string' || !baseUrl.startsWith('/')) {
|
||||
return baseUrl;
|
||||
|
|
@ -303,7 +308,7 @@ export default class ClientBase {
|
|||
}
|
||||
|
||||
if (response.ok) {
|
||||
return returnDataOnly ? response.data : response;
|
||||
return returnDataOnly ? (response.data || {}) : response;
|
||||
}
|
||||
|
||||
throw new ClientError(this.apiClient.baseUrl, {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientCategoriesMix {
|
||||
getCategories: (userId: string, teamId: string) => Promise<CategoriesWithOrder>;
|
||||
getCategoriesOrder: (userId: string, teamId: string) => Promise<string[]>;
|
||||
|
|
@ -8,7 +10,7 @@ export interface ClientCategoriesMix {
|
|||
updateChannelCategories: (userId: string, teamId: string, categories: CategoryWithChannels[]) => Promise<CategoriesWithOrder>;
|
||||
}
|
||||
|
||||
const ClientCategories = (superclass: any) => class extends superclass {
|
||||
const ClientCategories = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getCategories = async (userId: string, teamId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getCategoriesRoute(userId, teamId)}`,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientChannelsMix {
|
||||
getAllChannels: (page?: number, perPage?: number, notAssociatedToGroup?: string, excludeDefaultChannels?: boolean, includeTotalCount?: boolean) => Promise<any>;
|
||||
createChannel: (channel: Channel) => Promise<Channel>;
|
||||
|
|
@ -44,7 +46,7 @@ export interface ClientChannelsMix {
|
|||
getMemberInChannel: (channelId: string, userId: string) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientChannels = (superclass: any) => class extends superclass {
|
||||
const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getAllChannels = async (page = 0, perPage = PER_PAGE_DEFAULT, notAssociatedToGroup = '', excludeDefaultChannels = false, includeTotalCount = false) => {
|
||||
const queryData = {
|
||||
page,
|
||||
|
|
@ -60,7 +62,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
createChannel = async (channel: Channel) => {
|
||||
this.analytics.trackAPI('api_channels_create', {team_id: channel.team_id});
|
||||
this.analytics?.trackAPI('api_channels_create', {team_id: channel.team_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}`,
|
||||
|
|
@ -69,7 +71,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
createDirectChannel = async (userIds: string[]) => {
|
||||
this.analytics.trackAPI('api_channels_create_direct');
|
||||
this.analytics?.trackAPI('api_channels_create_direct');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/direct`,
|
||||
|
|
@ -78,7 +80,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
createGroupChannel = async (userIds: string[]) => {
|
||||
this.analytics.trackAPI('api_channels_create_group');
|
||||
this.analytics?.trackAPI('api_channels_create_group');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/group`,
|
||||
|
|
@ -87,7 +89,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
deleteChannel = async (channelId: string) => {
|
||||
this.analytics.trackAPI('api_channels_delete', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_channels_delete', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
|
|
@ -96,7 +98,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
unarchiveChannel = async (channelId: string) => {
|
||||
this.analytics.trackAPI('api_channels_unarchive', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_channels_unarchive', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/restore`,
|
||||
|
|
@ -105,7 +107,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
updateChannel = async (channel: Channel) => {
|
||||
this.analytics.trackAPI('api_channels_update', {channel_id: channel.id});
|
||||
this.analytics?.trackAPI('api_channels_update', {channel_id: channel.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channel.id)}`,
|
||||
|
|
@ -118,7 +120,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
updateChannelPrivacy = async (channelId: string, privacy: any) => {
|
||||
this.analytics.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
|
||||
this.analytics?.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/privacy`,
|
||||
|
|
@ -127,7 +129,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
patchChannel = async (channelId: string, channelPatch: Partial<Channel>) => {
|
||||
this.analytics.trackAPI('api_channels_patch', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_channels_patch', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/patch`,
|
||||
|
|
@ -136,7 +138,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
updateChannelNotifyProps = async (props: ChannelNotifyProps & {channel_id: string; user_id: string}) => {
|
||||
this.analytics.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
|
||||
this.analytics?.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(props.channel_id, props.user_id)}/notify_props`,
|
||||
|
|
@ -145,7 +147,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getChannel = async (channelId: string) => {
|
||||
this.analytics.trackAPI('api_channel_get', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_channel_get', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
|
|
@ -161,7 +163,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getChannelByNameAndTeamName = async (teamName: string, channelName: string, includeDeleted = false) => {
|
||||
this.analytics.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
|
||||
this.analytics?.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamNameRoute(teamName)}/channels/name/${channelName}?include_deleted=${includeDeleted}`,
|
||||
|
|
@ -243,7 +245,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
addToChannel = async (userId: string, channelId: string, postRootId = '') => {
|
||||
this.analytics.trackAPI('api_channels_add_member', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_channels_add_member', {channel_id: channelId});
|
||||
|
||||
const member = {user_id: userId, channel_id: channelId, post_root_id: postRootId};
|
||||
return this.doFetch(
|
||||
|
|
@ -253,7 +255,7 @@ const ClientChannels = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
removeFromChannel = async (userId: string, channelId: string) => {
|
||||
this.analytics.trackAPI('api_channels_remove_member', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_channels_remove_member', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(channelId, userId)}`,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientEmojisMix {
|
||||
getCustomEmoji: (id: string) => Promise<CustomEmoji>;
|
||||
getCustomEmojiByName: (name: string) => Promise<CustomEmoji>;
|
||||
|
|
@ -15,7 +17,7 @@ export interface ClientEmojisMix {
|
|||
autocompleteCustomEmoji: (name: string) => Promise<CustomEmoji[]>;
|
||||
}
|
||||
|
||||
const ClientEmojis = (superclass: any) => class extends superclass {
|
||||
const ClientEmojis = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getCustomEmoji = async (id: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getEmojisRoute()}/${id}`,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import {toMilliseconds} from '@utils/datetime';
|
||||
|
||||
import type ClientBase from './base';
|
||||
import type {ClientResponse, ClientResponseError, ProgressPromise, UploadRequestOptions} from '@mattermost/react-native-network-client';
|
||||
|
||||
export interface ClientFilesMix {
|
||||
|
|
@ -22,7 +23,7 @@ export interface ClientFilesMix {
|
|||
searchFilesWithParams: (teamId: string, FileSearchParams: string) => Promise<FileSearchRequest>;
|
||||
}
|
||||
|
||||
const ClientFiles = (superclass: any) => class extends superclass {
|
||||
const ClientFiles = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getFileUrl(fileId: string, timestamp: number) {
|
||||
let url = `${this.apiClient.baseUrl}${this.getFileRoute(fileId)}`;
|
||||
if (timestamp) {
|
||||
|
|
@ -76,13 +77,17 @@ const ClientFiles = (superclass: any) => class extends superclass {
|
|||
},
|
||||
timeoutInterval: toMilliseconds({minutes: 3}),
|
||||
};
|
||||
if (!file.localPath) {
|
||||
throw new Error('file does not have local path defined');
|
||||
}
|
||||
|
||||
const promise = this.apiClient.upload(url, file.localPath, options) as ProgressPromise<ClientResponse>;
|
||||
promise.progress!(onProgress).then(onComplete).catch(onError);
|
||||
return promise.cancel!;
|
||||
};
|
||||
|
||||
searchFilesWithParams = async (teamId: string, params: FileSearchParams) => {
|
||||
this.analytics.trackAPI('api_files_search');
|
||||
this.analytics?.trackAPI('api_files_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/files/search` : `${this.getFilesRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
import ClientError from './error';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
type PoliciesResponse<T> = {
|
||||
policies: T[];
|
||||
total_count: number;
|
||||
|
|
@ -25,7 +27,7 @@ export interface ClientGeneralMix {
|
|||
getRedirectLocation: (urlParam: string) => Promise<Record<string, string>>;
|
||||
}
|
||||
|
||||
const ClientGeneral = (superclass: any) => class extends superclass {
|
||||
const ClientGeneral = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getOpenGraphMetadata = async (url: string) => {
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/opengraph`,
|
||||
|
|
@ -49,7 +51,7 @@ const ClientGeneral = (superclass: any) => class extends superclass {
|
|||
const url = `${this.urlVersion}/logs`;
|
||||
|
||||
if (!this.enableLogging) {
|
||||
throw new ClientError(this.client.baseUrl, {
|
||||
throw new ClientError(this.apiClient.baseUrl, {
|
||||
message: 'Logging disabled.',
|
||||
url,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientGroupsMix {
|
||||
getGroup: (id: string) => Promise<Group>;
|
||||
getGroups: (params: {query?: string; filterAllowReference?: boolean; page?: number; perPage?: number; since?: number; includeMemberCount?: boolean}) => Promise<Group[]>;
|
||||
|
|
@ -16,7 +18,7 @@ export interface ClientGroupsMix {
|
|||
getAllTeamsAssociatedToGroup: (groupId: string, filterAllowReference?: boolean) => Promise<{groupTeams: GroupTeam[]}>;
|
||||
}
|
||||
|
||||
const ClientGroups = (superclass: any) => class extends superclass {
|
||||
const ClientGroups = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getGroup = async (id: string) => {
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/groups/${id}`,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientIntegrationsMix {
|
||||
getCommandsList: (teamId: string) => Promise<Command[]>;
|
||||
getCommandAutocompleteSuggestionsList: (userInput: string, teamId: string, channelId: string, rootId?: string) => Promise<AutocompleteSuggestion[]>;
|
||||
|
|
@ -14,7 +16,7 @@ export interface ClientIntegrationsMix {
|
|||
submitInteractiveDialog: (data: DialogSubmission) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientIntegrations = (superclass: any) => class extends superclass {
|
||||
const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getCommandsList = async (teamId: string) => {
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}?team_id=${teamId}`,
|
||||
|
|
@ -37,7 +39,7 @@ const ClientIntegrations = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
executeCommand = async (command: string, commandArgs = {}) => {
|
||||
this.analytics.trackAPI('api_integrations_used');
|
||||
this.analytics?.trackAPI('api_integrations_used');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}/execute`,
|
||||
|
|
@ -46,7 +48,7 @@ const ClientIntegrations = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
addCommand = async (command: Command) => {
|
||||
this.analytics.trackAPI('api_integrations_created');
|
||||
this.analytics?.trackAPI('api_integrations_created');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}`,
|
||||
|
|
@ -55,7 +57,7 @@ const ClientIntegrations = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
submitInteractiveDialog = async (data: DialogSubmission) => {
|
||||
this.analytics.trackAPI('api_interactive_messages_dialog_submitted');
|
||||
this.analytics?.trackAPI('api_interactive_messages_dialog_submitted');
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/actions/dialogs/submit`,
|
||||
{method: 'post', body: data},
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
import {General} from '@constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientNPSMix {
|
||||
npsGiveFeedbackAction: () => Promise<Post>;
|
||||
}
|
||||
|
||||
const ClientNPS = (superclass: any) => class extends superclass {
|
||||
const ClientNPS = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
npsGiveFeedbackAction = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPluginRoute(General.NPS_PLUGIN_ID)}/api/v1/give_feedback`,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPluginsMix {
|
||||
getPluginsManifests: () => Promise<ClientPluginManifest[]>;
|
||||
}
|
||||
|
||||
const ClientPlugins = (superclass: any) => class extends superclass {
|
||||
const ClientPlugins = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getPluginsManifests = async () => {
|
||||
return this.doFetch(
|
||||
`${this.getPluginsRoute()}/webapp`,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPostsMix {
|
||||
createPost: (post: Post) => Promise<Post>;
|
||||
updatePost: (post: Post) => Promise<Post>;
|
||||
|
|
@ -31,12 +33,12 @@ export interface ClientPostsMix {
|
|||
doPostActionWithCookie: (postId: string, actionId: string, actionCookie: string, selectedOption?: string) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientPosts = (superclass: any) => class extends superclass {
|
||||
const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
createPost = async (post: Post) => {
|
||||
this.analytics.trackAPI('api_posts_create', {channel_id: post.channel_id});
|
||||
this.analytics?.trackAPI('api_posts_create', {channel_id: post.channel_id});
|
||||
|
||||
if (post.root_id != null && post.root_id !== '') {
|
||||
this.analytics.trackAPI('api_posts_replied', {channel_id: post.channel_id});
|
||||
this.analytics?.trackAPI('api_posts_replied', {channel_id: post.channel_id});
|
||||
}
|
||||
|
||||
return this.doFetch(
|
||||
|
|
@ -46,7 +48,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
updatePost = async (post: Post) => {
|
||||
this.analytics.trackAPI('api_posts_update', {channel_id: post.channel_id});
|
||||
this.analytics?.trackAPI('api_posts_update', {channel_id: post.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(post.id)}`,
|
||||
|
|
@ -62,7 +64,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
patchPost = async (postPatch: Partial<Post> & {id: string}) => {
|
||||
this.analytics.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
|
||||
this.analytics?.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postPatch.id)}/patch`,
|
||||
|
|
@ -71,7 +73,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
deletePost = async (postId: string) => {
|
||||
this.analytics.trackAPI('api_posts_delete');
|
||||
this.analytics?.trackAPI('api_posts_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}`,
|
||||
|
|
@ -79,7 +81,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
);
|
||||
};
|
||||
|
||||
getPostThread = (postId: string, options: FetchPaginatedThreadOptions): Promise<PostResponse> => {
|
||||
getPostThread = (postId: string, options: FetchPaginatedThreadOptions) => {
|
||||
const {
|
||||
fetchThreads = true,
|
||||
collapsedThreads = false,
|
||||
|
|
@ -110,7 +112,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getPostsBefore = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics.trackAPI('api_posts_get_before', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_posts_get_before', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({before: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
|
|
@ -119,7 +121,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getPostsAfter = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics.trackAPI('api_posts_get_after', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_posts_get_after', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({after: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
|
|
@ -135,7 +137,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getSavedPosts = async (userId: string, channelId = '', teamId = '', page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics.trackAPI('api_posts_get_flagged', {team_id: teamId});
|
||||
this.analytics?.trackAPI('api_posts_get_flagged', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/flagged${buildQueryString({channel_id: channelId, team_id: teamId, page, per_page: perPage})}`,
|
||||
|
|
@ -144,7 +146,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getPinnedPosts = async (channelId: string) => {
|
||||
this.analytics.trackAPI('api_posts_get_pinned', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_posts_get_pinned', {channel_id: channelId});
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/pinned`,
|
||||
{method: 'get'},
|
||||
|
|
@ -152,7 +154,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
markPostAsUnread = async (userId: string, postId: string) => {
|
||||
this.analytics.trackAPI('api_post_set_unread_post');
|
||||
this.analytics?.trackAPI('api_post_set_unread_post');
|
||||
|
||||
// collapsed_threads_supported is not based on user preferences but to know if "CLIENT" supports CRT
|
||||
const body = JSON.stringify({collapsed_threads_supported: true});
|
||||
|
|
@ -164,7 +166,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
pinPost = async (postId: string) => {
|
||||
this.analytics.trackAPI('api_posts_pin');
|
||||
this.analytics?.trackAPI('api_posts_pin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/pin`,
|
||||
|
|
@ -173,7 +175,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
unpinPost = async (postId: string) => {
|
||||
this.analytics.trackAPI('api_posts_unpin');
|
||||
this.analytics?.trackAPI('api_posts_unpin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/unpin`,
|
||||
|
|
@ -182,7 +184,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
addReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics.trackAPI('api_reactions_save', {post_id: postId});
|
||||
this.analytics?.trackAPI('api_reactions_save', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getReactionsRoute()}`,
|
||||
|
|
@ -191,7 +193,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
removeReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics.trackAPI('api_reactions_delete', {post_id: postId});
|
||||
this.analytics?.trackAPI('api_reactions_delete', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/${postId}/reactions/${emojiName}`,
|
||||
|
|
@ -207,7 +209,7 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
searchPostsWithParams = async (teamId: string, params: PostSearchParams) => {
|
||||
this.analytics.trackAPI('api_posts_search');
|
||||
this.analytics?.trackAPI('api_posts_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/posts/search` : `${this.getPostsRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
|
|
@ -222,9 +224,9 @@ const ClientPosts = (superclass: any) => class extends superclass {
|
|||
|
||||
doPostActionWithCookie = async (postId: string, actionId: string, actionCookie: string, selectedOption = '') => {
|
||||
if (selectedOption) {
|
||||
this.analytics.trackAPI('api_interactive_messages_menu_selected');
|
||||
this.analytics?.trackAPI('api_interactive_messages_menu_selected');
|
||||
} else {
|
||||
this.analytics.trackAPI('api_interactive_messages_button_clicked');
|
||||
this.analytics?.trackAPI('api_interactive_messages_button_clicked');
|
||||
}
|
||||
|
||||
const msg: any = {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientPreferencesMix {
|
||||
savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
|
||||
deletePreferences: (userId: string, preferences: PreferenceType[]) => Promise<any>;
|
||||
getMyPreferences: () => Promise<PreferenceType[]>;
|
||||
}
|
||||
|
||||
const ClientPreferences = (superclass: any) => class extends superclass {
|
||||
const ClientPreferences = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
savePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics.trackAPI('action_posts_flag');
|
||||
this.analytics?.trackAPI('action_posts_flag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}`,
|
||||
{method: 'put', body: preferences},
|
||||
|
|
@ -24,7 +26,7 @@ const ClientPreferences = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
deletePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics.trackAPI('action_posts_unflag');
|
||||
this.analytics?.trackAPI('action_posts_unflag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}/delete`,
|
||||
{method: 'post', body: preferences},
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientTeamsMix {
|
||||
createTeam: (team: Team) => Promise<Team>;
|
||||
deleteTeam: (teamId: string) => Promise<any>;
|
||||
|
|
@ -28,9 +30,9 @@ export interface ClientTeamsMix {
|
|||
getTeamIconUrl: (teamId: string, lastTeamIconUpdate: number) => string;
|
||||
}
|
||||
|
||||
const ClientTeams = (superclass: any) => class extends superclass {
|
||||
const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
createTeam = async (team: Team) => {
|
||||
this.analytics.trackAPI('api_teams_create');
|
||||
this.analytics?.trackAPI('api_teams_create');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamsRoute()}`,
|
||||
|
|
@ -39,7 +41,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
deleteTeam = async (teamId: string) => {
|
||||
this.analytics.trackAPI('api_teams_delete');
|
||||
this.analytics?.trackAPI('api_teams_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}`,
|
||||
|
|
@ -48,7 +50,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
updateTeam = async (team: Team) => {
|
||||
this.analytics.trackAPI('api_teams_update_name', {team_id: team.id});
|
||||
this.analytics?.trackAPI('api_teams_update_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}`,
|
||||
|
|
@ -57,7 +59,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
patchTeam = async (team: Partial<Team> & {id: string}) => {
|
||||
this.analytics.trackAPI('api_teams_patch_name', {team_id: team.id});
|
||||
this.analytics?.trackAPI('api_teams_patch_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}/patch`,
|
||||
|
|
@ -80,7 +82,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getTeamByName = async (teamName: string) => {
|
||||
this.analytics.trackAPI('api_teams_get_team_by_name');
|
||||
this.analytics?.trackAPI('api_teams_get_team_by_name');
|
||||
|
||||
return this.doFetch(
|
||||
this.getTeamNameRoute(teamName),
|
||||
|
|
@ -131,7 +133,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
addToTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
const member = {user_id: userId, team_id: teamId};
|
||||
return this.doFetch(
|
||||
|
|
@ -141,7 +143,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
addUsersToTeamGracefully = (teamId: string, userIds: string[]) => {
|
||||
this.analytics.trackAPI('api_teams_batch_add_members', {team_id: teamId, count: userIds.length});
|
||||
this.analytics?.trackAPI('api_teams_batch_add_members', {team_id: teamId, count: userIds.length});
|
||||
|
||||
const members: Array<{team_id: string; user_id: string}> = [];
|
||||
userIds.forEach((id) => members.push({team_id: teamId, user_id: id}));
|
||||
|
|
@ -153,7 +155,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
sendEmailInvitesToTeamGracefully = (teamId: string, emails: string[]) => {
|
||||
this.analytics.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}/invite/email?graceful=true`,
|
||||
|
|
@ -170,7 +172,7 @@ const ClientTeams = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
removeFromTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics.trackAPI('api_teams_remove_members', {team_id: teamId});
|
||||
this.analytics?.trackAPI('api_teams_remove_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamMemberRoute(teamId, userId)}`,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import {buildQueryString, isMinimumServerVersion} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientThreadsMix {
|
||||
getThreads: (userId: string, teamId: string, before?: string, after?: string, pageSize?: number, deleted?: boolean, unread?: boolean, since?: number, totalsOnly?: boolean, serverVersion?: string) => Promise<GetUserThreadsResponse>;
|
||||
getThread: (userId: string, teamId: string, threadId: string, extended?: boolean) => Promise<any>;
|
||||
|
|
@ -14,7 +16,7 @@ export interface ClientThreadsMix {
|
|||
updateThreadFollow: (userId: string, teamId: string, threadId: string, state: boolean) => Promise<any>;
|
||||
}
|
||||
|
||||
const ClientThreads = (superclass: any) => class extends superclass {
|
||||
const ClientThreads = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
getThreads = async (userId: string, teamId: string, before = '', after = '', pageSize = PER_PAGE_DEFAULT, deleted = false, unread = false, since = 0, totalsOnly = false, serverVersion = '') => {
|
||||
const queryStringObj: Record<string, any> = {
|
||||
extended: 'true',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientTosMix {
|
||||
updateMyTermsOfServiceStatus: (termsOfServiceId: string, accepted: boolean) => Promise<{status: string}>;
|
||||
getTermsOfService: () => Promise<TermsOfService>;
|
||||
}
|
||||
|
||||
const ClientTos = (superclass: any) => class extends superclass {
|
||||
const ClientTos = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
updateMyTermsOfServiceStatus = async (termsOfServiceId: string, accepted: boolean) => {
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute('me')}/terms_of_service`,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import {buildQueryString} from '@utils/helpers';
|
|||
|
||||
import {PER_PAGE_DEFAULT} from './constants';
|
||||
|
||||
import type ClientBase from './base';
|
||||
|
||||
export interface ClientUsersMix {
|
||||
createUser: (user: UserProfile, token: string, inviteId: string) => Promise<UserProfile>;
|
||||
patchMe: (userPatch: Partial<UserProfile>) => Promise<UserProfile>;
|
||||
|
|
@ -46,9 +48,9 @@ export interface ClientUsersMix {
|
|||
removeRecentCustomStatus: (customStatus: UserCustomStatus) => Promise<{status: string}>;
|
||||
}
|
||||
|
||||
const ClientUsers = (superclass: any) => class extends superclass {
|
||||
const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
createUser = async (user: UserProfile, token: string, inviteId: string) => {
|
||||
this.analytics.trackAPI('api_users_create');
|
||||
this.analytics?.trackAPI('api_users_create');
|
||||
|
||||
const queryParams: any = {};
|
||||
|
||||
|
|
@ -74,7 +76,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
patchUser = async (userPatch: Partial<UserProfile> & {id: string}) => {
|
||||
this.analytics.trackAPI('api_users_patch');
|
||||
this.analytics?.trackAPI('api_users_patch');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userPatch.id)}/patch`,
|
||||
|
|
@ -83,7 +85,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
updateUser = async (user: UserProfile) => {
|
||||
this.analytics.trackAPI('api_users_update');
|
||||
this.analytics?.trackAPI('api_users_update');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(user.id)}`,
|
||||
|
|
@ -92,7 +94,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
demoteUserToGuest = async (userId: string) => {
|
||||
this.analytics.trackAPI('api_users_demote_user_to_guest');
|
||||
this.analytics?.trackAPI('api_users_demote_user_to_guest');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/demote`,
|
||||
|
|
@ -101,7 +103,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getKnownUsers = async () => {
|
||||
this.analytics.trackAPI('api_get_known_users');
|
||||
this.analytics?.trackAPI('api_get_known_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/known`,
|
||||
|
|
@ -110,7 +112,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
sendPasswordResetEmail = async (email: string) => {
|
||||
this.analytics.trackAPI('api_users_send_password_reset');
|
||||
this.analytics?.trackAPI('api_users_send_password_reset');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/password/reset/send`,
|
||||
|
|
@ -119,7 +121,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
setDefaultProfileImage = async (userId: string) => {
|
||||
this.analytics.trackAPI('api_users_set_default_profile_picture');
|
||||
this.analytics?.trackAPI('api_users_set_default_profile_picture');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/image`,
|
||||
|
|
@ -128,10 +130,10 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
login = async (loginId: string, password: string, token = '', deviceId = '', ldapOnly = false) => {
|
||||
this.analytics.trackAPI('api_users_login');
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
|
||||
if (ldapOnly) {
|
||||
this.analytics.trackAPI('api_users_login_ldap');
|
||||
this.analytics?.trackAPI('api_users_login_ldap');
|
||||
}
|
||||
|
||||
const body: any = {
|
||||
|
|
@ -159,7 +161,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
loginById = async (id: string, password: string, token = '', deviceId = '') => {
|
||||
this.analytics.trackAPI('api_users_login');
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
const body: any = {
|
||||
device_id: deviceId,
|
||||
id,
|
||||
|
|
@ -181,7 +183,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
logout = async () => {
|
||||
this.analytics.trackAPI('api_users_logout');
|
||||
this.analytics?.trackAPI('api_users_logout');
|
||||
|
||||
const response = await this.doFetch(
|
||||
`${this.getUsersRoute()}/logout`,
|
||||
|
|
@ -192,7 +194,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfiles = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics.trackAPI('api_profiles_get');
|
||||
this.analytics?.trackAPI('api_profiles_get');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({page, per_page: perPage, ...options})}`,
|
||||
|
|
@ -201,7 +203,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesByIds = async (userIds: string[], options = {}) => {
|
||||
this.analytics.trackAPI('api_profiles_get_by_ids');
|
||||
this.analytics?.trackAPI('api_profiles_get_by_ids');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/ids${buildQueryString(options)}`,
|
||||
|
|
@ -210,7 +212,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesByUsernames = async (usernames: string[]) => {
|
||||
this.analytics.trackAPI('api_profiles_get_by_usernames');
|
||||
this.analytics?.trackAPI('api_profiles_get_by_usernames');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/usernames`,
|
||||
|
|
@ -219,7 +221,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesInTeam = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '', options = {}) => {
|
||||
this.analytics.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
||||
this.analytics?.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, in_team: teamId, page, per_page: perPage, sort})}`,
|
||||
|
|
@ -228,7 +230,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesNotInTeam = async (teamId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {not_in_team: teamId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
|
|
@ -242,7 +244,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesWithoutTeam = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics.trackAPI('api_profiles_get_without_team');
|
||||
this.analytics?.trackAPI('api_profiles_get_without_team');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, without_team: 1, page, per_page: perPage})}`,
|
||||
|
|
@ -251,7 +253,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesInChannel = async (channelId: string, options: GetUsersOptions) => {
|
||||
this.analytics.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
||||
this.analytics?.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
||||
|
||||
const queryStringObj = {in_channel: channelId, ...options};
|
||||
return this.doFetch(
|
||||
|
|
@ -261,7 +263,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesInGroupChannels = async (channelsIds: string[]) => {
|
||||
this.analytics.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
||||
this.analytics?.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/group_channels`,
|
||||
|
|
@ -270,7 +272,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
getProfilesNotInChannel = async (teamId: string, channelId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {in_team: teamId, not_in_channel: channelId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
|
|
@ -372,7 +374,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
|
|||
};
|
||||
|
||||
searchUsers = async (term: string, options: any) => {
|
||||
this.analytics.trackAPI('api_search_users');
|
||||
this.analytics?.trackAPI('api_search_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/search`,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ describe('** APP DATA OPERATOR **', () => {
|
|||
],
|
||||
tableName: 'Info',
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}, 'handleInfo');
|
||||
});
|
||||
|
||||
it('=> HandleGlobal: should write to GLOBAL table', async () => {
|
||||
|
|
@ -79,6 +79,6 @@ describe('** APP DATA OPERATOR **', () => {
|
|||
createOrUpdateRawValues: globals,
|
||||
tableName: 'Global',
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}, 'handleGlobal');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default class AppDataOperator extends BaseDataOperator {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: info, key: 'version_number'}),
|
||||
tableName: INFO,
|
||||
});
|
||||
}, 'handleInfo');
|
||||
};
|
||||
|
||||
handleGlobal = async ({globals, prepareRecordsOnly = true}: HandleGlobalArgs) => {
|
||||
|
|
@ -45,6 +45,6 @@ export default class AppDataOperator extends BaseDataOperator {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: globals, key: 'id'}),
|
||||
tableName: GLOBAL,
|
||||
});
|
||||
}, 'handleGlobal');
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ import type {
|
|||
|
||||
export interface BaseDataOperatorType {
|
||||
database: Database;
|
||||
handleRecords: ({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues, tableName, prepareRecordsOnly}: HandleRecordsArgs) => Promise<Model[]>;
|
||||
processRecords: ({createOrUpdateRawValues, deleteRawValues, tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs) => Promise<ProcessRecordResults>;
|
||||
batchRecords: (models: Model[]) => Promise<void>;
|
||||
prepareRecords: ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs) => Promise<Model[]>;
|
||||
handleRecords: <T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues, tableName, prepareRecordsOnly}: HandleRecordsArgs<T>, description: string) => Promise<Model[]>;
|
||||
processRecords: <T extends Model>({createOrUpdateRawValues, deleteRawValues, tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs) => Promise<ProcessRecordResults<T>>;
|
||||
batchRecords: (models: Model[], description: string) => Promise<void>;
|
||||
prepareRecords: <T extends Model>({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs<T>) => Promise<Model[]>;
|
||||
}
|
||||
|
||||
export default class BaseDataOperator {
|
||||
|
|
@ -45,7 +45,7 @@ export default class BaseDataOperator {
|
|||
* @param {(existing: Model, newElement: RawValue) => boolean} inputsArg.buildKeyRecordBy
|
||||
* @returns {Promise<{ProcessRecordResults}>}
|
||||
*/
|
||||
processRecords = async ({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs): Promise<ProcessRecordResults> => {
|
||||
processRecords = async <T extends Model>({createOrUpdateRawValues = [], deleteRawValues = [], tableName, buildKeyRecordBy, fieldName}: ProcessRecordsArgs): Promise<ProcessRecordResults<T>> => {
|
||||
const getRecords = async (rawValues: RawValue[]) => {
|
||||
// We will query a table where one of its fields can match a range of values. Hence, here we are extracting all those potential values.
|
||||
const columnValues: string[] = getRangeOfValues({fieldName, raws: rawValues});
|
||||
|
|
@ -60,7 +60,7 @@ export default class BaseDataOperator {
|
|||
return [];
|
||||
}
|
||||
|
||||
const existingRecords = await retrieveRecords({
|
||||
const existingRecords = await retrieveRecords<T>({
|
||||
database: this.database,
|
||||
tableName,
|
||||
condition: Q.where(fieldName, Q.oneOf(columnValues)),
|
||||
|
|
@ -125,13 +125,13 @@ export default class BaseDataOperator {
|
|||
* @param {(TransformerArgs) => Promise<Model>;} transformer
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
prepareRecords = async ({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs): Promise<Model[]> => {
|
||||
prepareRecords = async <T extends Model>({tableName, createRaws, deleteRaws, updateRaws, transformer}: OperationArgs<T>): Promise<T[]> => {
|
||||
if (!this.database) {
|
||||
logWarning('Database not defined in prepareRecords');
|
||||
return [];
|
||||
}
|
||||
|
||||
let preparedRecords: Array<Promise<Model>> = [];
|
||||
let preparedRecords: Array<Promise<T>> = [];
|
||||
|
||||
// create operation
|
||||
if (createRaws?.length) {
|
||||
|
|
@ -165,7 +165,7 @@ export default class BaseDataOperator {
|
|||
preparedRecords = preparedRecords.concat(recordPromises);
|
||||
}
|
||||
|
||||
const results = await Promise.all(preparedRecords);
|
||||
const results = (await Promise.all(preparedRecords)) as T[];
|
||||
|
||||
if (deleteRaws?.length) {
|
||||
deleteRaws.forEach((deleteRecord) => {
|
||||
|
|
@ -182,12 +182,12 @@ export default class BaseDataOperator {
|
|||
* @param {Array} models
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async batchRecords(models: Model[]): Promise<void> {
|
||||
async batchRecords(models: Model[], description: string): Promise<void> {
|
||||
try {
|
||||
if (models.length > 0) {
|
||||
await this.database.write(async (writer) => {
|
||||
await writer.batch(...models);
|
||||
});
|
||||
await writer.batch(models);
|
||||
}, description);
|
||||
}
|
||||
} catch (e) {
|
||||
logWarning('batchRecords error ', e as Error);
|
||||
|
|
@ -205,7 +205,7 @@ export default class BaseDataOperator {
|
|||
* @param {string} handleRecordsArgs.tableName
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
async handleRecords({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true}: HandleRecordsArgs): Promise<Model[]> {
|
||||
async handleRecords<T extends Model>({buildKeyRecordBy, fieldName, transformer, createOrUpdateRawValues, deleteRawValues = [], tableName, prepareRecordsOnly = true}: HandleRecordsArgs<T>, description: string): Promise<T[]> {
|
||||
if (!createOrUpdateRawValues.length) {
|
||||
logWarning(
|
||||
`An empty "rawValues" array has been passed to the handleRecords method for tableName ${tableName}`,
|
||||
|
|
@ -213,7 +213,7 @@ export default class BaseDataOperator {
|
|||
return [];
|
||||
}
|
||||
|
||||
const {createRaws, deleteRaws, updateRaws} = await this.processRecords({
|
||||
const {createRaws, deleteRaws, updateRaws} = await this.processRecords<T>({
|
||||
createOrUpdateRawValues,
|
||||
deleteRawValues,
|
||||
tableName,
|
||||
|
|
@ -221,8 +221,8 @@ export default class BaseDataOperator {
|
|||
fieldName,
|
||||
});
|
||||
|
||||
let models: Model[] = [];
|
||||
models = await this.prepareRecords({
|
||||
let models: T[] = [];
|
||||
models = await this.prepareRecords<T>({
|
||||
tableName,
|
||||
createRaws,
|
||||
updateRaws,
|
||||
|
|
@ -231,7 +231,7 @@ export default class BaseDataOperator {
|
|||
});
|
||||
|
||||
if (!prepareRecordsOnly && models?.length) {
|
||||
await this.batchRecords(models);
|
||||
await this.batchRecords(models, description);
|
||||
}
|
||||
|
||||
return models;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ describe('*** Operator: Category Handlers tests ***', () => {
|
|||
tableName: MM_TABLES.SERVER.CATEGORY,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformCategoryRecord,
|
||||
});
|
||||
}, 'handleCategories');
|
||||
});
|
||||
|
||||
it('=> handleCategoryChannels: should write to the CATEGORY_CHANNEL table', async () => {
|
||||
|
|
@ -74,6 +74,6 @@ describe('*** Operator: Category Handlers tests ***', () => {
|
|||
tableName: MM_TABLES.SERVER.CATEGORY_CHANNEL,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformCategoryChannelRecord,
|
||||
});
|
||||
}, 'handleCategoryChannels');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {
|
||||
HandleCategoryChannelArgs,
|
||||
HandleCategoryArgs,
|
||||
|
|
@ -28,7 +29,7 @@ export interface CategoryHandlerMix {
|
|||
handleCategories: ({categories, prepareRecordsOnly}: HandleCategoryArgs) => Promise<CategoryModel[]>;
|
||||
}
|
||||
|
||||
const CategoryHandler = (superclass: any) => class extends superclass {
|
||||
const CategoryHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
/**
|
||||
* handleCategories: Handler responsible for the Create/Update operations occurring on the Category table from the 'Server' schema
|
||||
* @param {HandleCategoryArgs} categoriesArgs
|
||||
|
|
@ -78,7 +79,7 @@ const CategoryHandler = (superclass: any) => class extends superclass {
|
|||
createOrUpdateRawValues,
|
||||
tableName: CATEGORY,
|
||||
prepareRecordsOnly,
|
||||
});
|
||||
}, 'handleCategories');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -88,7 +89,7 @@ const CategoryHandler = (superclass: any) => class extends superclass {
|
|||
* @param {boolean} categoriesArgs.prepareRecordsOnly
|
||||
* @returns {Promise<CategoryChannelModel[]>}
|
||||
*/
|
||||
handleCategoryChannels = async ({categoryChannels, prepareRecordsOnly = true}: HandleCategoryChannelArgs): Promise<CategoryModel[]> => {
|
||||
handleCategoryChannels = async ({categoryChannels, prepareRecordsOnly = true}: HandleCategoryChannelArgs): Promise<CategoryChannelModel[]> => {
|
||||
if (!categoryChannels?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "categoryChannels" array has been passed to the handleCategories method',
|
||||
|
|
@ -105,7 +106,7 @@ const CategoryHandler = (superclass: any) => class extends superclass {
|
|||
createOrUpdateRawValues,
|
||||
tableName: CATEGORY_CHANNEL,
|
||||
prepareRecordsOnly,
|
||||
});
|
||||
}, 'handleCategoryChannels');
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
|||
tableName: 'Channel',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformChannelRecord,
|
||||
});
|
||||
}, 'handleChannel');
|
||||
});
|
||||
|
||||
it('=> HandleMyChannelSettings: should write to the MY_CHANNEL_SETTINGS table', async () => {
|
||||
|
|
@ -103,7 +103,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
|||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildMyChannelKey,
|
||||
transformer: transformMyChannelSettingsRecord,
|
||||
});
|
||||
}, 'handleMyChannelSettings');
|
||||
});
|
||||
|
||||
it('=> HandleChannelInfo: should write to the CHANNEL_INFO table', async () => {
|
||||
|
|
@ -134,7 +134,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
|||
tableName: 'ChannelInfo',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformChannelInfoRecord,
|
||||
});
|
||||
}, 'handleChannelInfo');
|
||||
});
|
||||
|
||||
it('=> HandleMyChannel: should write to the MY_CHANNEL table', async () => {
|
||||
|
|
@ -195,7 +195,7 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
|||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildMyChannelKey,
|
||||
transformer: transformMyChannelRecord,
|
||||
});
|
||||
}, 'handleMyChannel');
|
||||
});
|
||||
|
||||
it('=> HandleMyChannel: should keep the previous lastFetchedAt for MY_CHANNEL', async () => {
|
||||
|
|
@ -314,6 +314,6 @@ describe('*** Operator: Channel Handlers tests ***', () => {
|
|||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildChannelMembershipKey,
|
||||
transformer: transformChannelMembershipRecord,
|
||||
});
|
||||
}, 'handleChannelMembership');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {getUniqueRawsBy} from '@database/operator/utils/general';
|
|||
import {getIsCRTEnabled} from '@queries/servers/thread';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {HandleChannelArgs, HandleChannelInfoArgs, HandleChannelMembershipArgs, HandleMyChannelArgs, HandleMyChannelSettingsArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type ChannelInfoModel from '@typings/database/models/servers/channel_info';
|
||||
|
|
@ -42,7 +43,7 @@ export interface ChannelHandlerMix {
|
|||
handleMyChannel: ({channels, myChannels, isCRTEnabled, prepareRecordsOnly}: HandleMyChannelArgs) => Promise<MyChannelModel[]>;
|
||||
}
|
||||
|
||||
const ChannelHandler = (superclass: any) => class extends superclass {
|
||||
const ChannelHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
/**
|
||||
* handleChannel: Handler responsible for the Create/Update operations occurring on the CHANNEL table from the 'Server' schema
|
||||
* @param {HandleChannelArgs} channelsArgs
|
||||
|
|
@ -89,7 +90,7 @@ const ChannelHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: CHANNEL,
|
||||
});
|
||||
}, 'handleChannel');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -146,7 +147,7 @@ const ChannelHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: MY_CHANNEL_SETTINGS,
|
||||
});
|
||||
}, 'handleMyChannelSettings');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -205,7 +206,7 @@ const ChannelHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: CHANNEL_INFO,
|
||||
});
|
||||
}, 'handleChannelInfo');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -292,7 +293,7 @@ const ChannelHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: MY_CHANNEL,
|
||||
});
|
||||
}, 'handleMyChannel');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -348,7 +349,7 @@ const ChannelHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: CHANNEL_MEMBERSHIP,
|
||||
});
|
||||
}, 'handleChannelMembership');
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,6 @@ describe('*** Operator: Group Handlers tests ***', () => {
|
|||
tableName: MM_TABLES.SERVER.GROUP,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformGroupRecord,
|
||||
});
|
||||
}, 'handleGroups');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {queryGroupChannelForChannel, queryGroupMembershipForMember, queryGroupTe
|
|||
import {generateGroupAssociationId} from '@utils/groups';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {HandleGroupArgs, HandleGroupChannelsForChannelArgs, HandleGroupMembershipForMemberArgs, HandleGroupTeamsForTeamArgs} from '@typings/database/database';
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
import type GroupChannelModel from '@typings/database/models/servers/group_channel';
|
||||
|
|
@ -23,7 +24,7 @@ export interface GroupHandlerMix {
|
|||
handleGroupTeamsForTeam: ({teamId, groups, prepareRecordsOnly}: HandleGroupTeamsForTeamArgs) => Promise<GroupTeamModel[]>;
|
||||
}
|
||||
|
||||
const GroupHandler = (superclass: any) => class extends superclass implements GroupHandlerMix {
|
||||
const GroupHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass implements GroupHandlerMix {
|
||||
/**
|
||||
* handleGroups: Handler responsible for the Create/Update operations occurring on the Group table from the 'Server' schema
|
||||
*
|
||||
|
|
@ -46,7 +47,7 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
|
|||
createOrUpdateRawValues,
|
||||
tableName: GROUP,
|
||||
prepareRecordsOnly,
|
||||
});
|
||||
}, 'handleGroups');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -93,14 +94,14 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
|
|||
records.push(...(await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupChannelRecord,
|
||||
rawValues,
|
||||
createOrUpdateRawValues: rawValues,
|
||||
tableName: GROUP_CHANNEL,
|
||||
prepareRecordsOnly: true,
|
||||
})));
|
||||
}, 'handleGroupChannelsForChannel')));
|
||||
|
||||
// Batch update if there are records
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records);
|
||||
await this.batchRecords(records, 'handleGroupChannelsForChannel');
|
||||
}
|
||||
|
||||
return records;
|
||||
|
|
@ -150,14 +151,14 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
|
|||
records.push(...(await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupMembershipRecord,
|
||||
rawValues,
|
||||
createOrUpdateRawValues: rawValues,
|
||||
tableName: GROUP_MEMBERSHIP,
|
||||
prepareRecordsOnly: true,
|
||||
})));
|
||||
}, 'handleGroupMembershipsForMember')));
|
||||
|
||||
// Batch update if there are records
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records);
|
||||
await this.batchRecords(records, 'handleGroupMembershipsForMember');
|
||||
}
|
||||
|
||||
return records;
|
||||
|
|
@ -207,14 +208,14 @@ const GroupHandler = (superclass: any) => class extends superclass implements Gr
|
|||
records.push(...(await this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupTeamRecord,
|
||||
rawValues,
|
||||
createOrUpdateRawValues: rawValues,
|
||||
tableName: GROUP_TEAM,
|
||||
prepareRecordsOnly: true,
|
||||
})));
|
||||
}, 'handleGroupTeamsForTeam')));
|
||||
|
||||
// Batch update if there are records
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records);
|
||||
await this.batchRecords(records, 'handleGroupTeamsForTeam');
|
||||
}
|
||||
|
||||
return records;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ describe('*** DataOperator: Base Handlers tests ***', () => {
|
|||
createOrUpdateRawValues: roles,
|
||||
tableName: 'Role',
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}, 'handleRole');
|
||||
});
|
||||
|
||||
it('=> HandleCustomEmojis: should write to the CUSTOM_EMOJI table', async () => {
|
||||
|
|
@ -72,7 +72,7 @@ describe('*** DataOperator: Base Handlers tests ***', () => {
|
|||
tableName: 'CustomEmoji',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformCustomEmojiRecord,
|
||||
});
|
||||
}, 'handleCustomEmojis');
|
||||
});
|
||||
|
||||
it('=> HandleSystem: should write to the SYSTEM table', async () => {
|
||||
|
|
@ -93,7 +93,7 @@ describe('*** DataOperator: Base Handlers tests ***', () => {
|
|||
createOrUpdateRawValues: systems,
|
||||
tableName: 'System',
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}, 'handleSystem');
|
||||
});
|
||||
|
||||
it('=> HandleConfig: should write to the CONFIG table', async () => {
|
||||
|
|
@ -117,7 +117,7 @@ describe('*** DataOperator: Base Handlers tests ***', () => {
|
|||
tableName: 'Config',
|
||||
prepareRecordsOnly: false,
|
||||
deleteRawValues: configsToDelete,
|
||||
});
|
||||
}, 'handleConfigs');
|
||||
});
|
||||
|
||||
it('=> No table name: should not call execute if tableName is invalid', async () => {
|
||||
|
|
@ -135,7 +135,7 @@ describe('*** DataOperator: Base Handlers tests ***', () => {
|
|||
transformer: transformSystemRecord,
|
||||
createOrUpdateRawValues: [{id: 'tos-1', value: '1'}],
|
||||
prepareRecordsOnly: false,
|
||||
}),
|
||||
}, 'test'),
|
||||
).rejects.toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,13 +12,17 @@ import {
|
|||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import {sanitizeReactions} from '../../utils/reaction';
|
||||
import {transformReactionRecord} from '../transformers/reaction';
|
||||
|
||||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type {HandleConfigArgs, HandleCustomEmojiArgs, HandleRoleArgs, HandleSystemArgs, OperationArgs} from '@typings/database/database';
|
||||
import type {HandleConfigArgs, HandleCustomEmojiArgs, HandleReactionsArgs, HandleRoleArgs, HandleSystemArgs, OperationArgs} from '@typings/database/database';
|
||||
import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji';
|
||||
import type ReactionModel from '@typings/database/models/servers/reaction';
|
||||
import type RoleModel from '@typings/database/models/servers/role';
|
||||
import type SystemModel from '@typings/database/models/servers/system';
|
||||
|
||||
const {SERVER: {CONFIG, CUSTOM_EMOJI, ROLE, SYSTEM}} = MM_TABLES;
|
||||
const {SERVER: {CONFIG, CUSTOM_EMOJI, ROLE, SYSTEM, REACTION}} = MM_TABLES;
|
||||
|
||||
export default class ServerDataOperatorBase extends BaseDataOperator {
|
||||
handleRole = async ({roles, prepareRecordsOnly = true}: HandleRoleArgs) => {
|
||||
|
|
@ -35,7 +39,7 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: roles, key: 'id'}),
|
||||
tableName: ROLE,
|
||||
}) as Promise<RoleModel[]>;
|
||||
}, 'handleRole') as Promise<RoleModel[]>;
|
||||
};
|
||||
|
||||
handleCustomEmojis = async ({emojis, prepareRecordsOnly = true}: HandleCustomEmojiArgs) => {
|
||||
|
|
@ -52,7 +56,7 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: emojis, key: 'name'}),
|
||||
tableName: CUSTOM_EMOJI,
|
||||
}) as Promise<CustomEmojiModel[]>;
|
||||
}, 'handleCustomEmojis') as Promise<CustomEmojiModel[]>;
|
||||
};
|
||||
|
||||
handleSystem = async ({systems, prepareRecordsOnly = true}: HandleSystemArgs) => {
|
||||
|
|
@ -69,7 +73,7 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues: getUniqueRawsBy({raws: systems, key: 'id'}),
|
||||
tableName: SYSTEM,
|
||||
}) as Promise<SystemModel[]>;
|
||||
}, 'handleSystem') as Promise<SystemModel[]>;
|
||||
};
|
||||
|
||||
handleConfigs = async ({configs, configsToDelete, prepareRecordsOnly = true}: HandleConfigArgs) => {
|
||||
|
|
@ -87,7 +91,64 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
|
|||
createOrUpdateRawValues: getUniqueRawsBy({raws: configs, key: 'id'}),
|
||||
tableName: CONFIG,
|
||||
deleteRawValues: configsToDelete,
|
||||
});
|
||||
}, 'handleConfigs');
|
||||
};
|
||||
|
||||
/**
|
||||
* handleReactions: Handler responsible for the Create/Update operations occurring on the Reaction table from the 'Server' schema
|
||||
* @param {HandleReactionsArgs} handleReactions
|
||||
* @param {ReactionsPerPost[]} handleReactions.postsReactions
|
||||
* @param {boolean} handleReactions.prepareRecordsOnly
|
||||
* @param {boolean} handleReactions.skipSync
|
||||
* @returns {Promise<Array<(ReactionModel | CustomEmojiModel)>>}
|
||||
*/
|
||||
handleReactions = async ({postsReactions, prepareRecordsOnly, skipSync}: HandleReactionsArgs): Promise<ReactionModel[]> => {
|
||||
const batchRecords: ReactionModel[] = [];
|
||||
|
||||
if (!postsReactions?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "postsReactions" array has been passed to the handleReactions method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
for await (const postReactions of postsReactions) {
|
||||
const {post_id, reactions} = postReactions;
|
||||
const {
|
||||
createReactions,
|
||||
deleteReactions,
|
||||
} = await sanitizeReactions({
|
||||
database: this.database,
|
||||
post_id,
|
||||
rawReactions: reactions,
|
||||
skipSync,
|
||||
});
|
||||
|
||||
if (createReactions?.length) {
|
||||
// Prepares record for model Reactions
|
||||
const reactionsRecords = (await this.prepareRecords({
|
||||
createRaws: createReactions,
|
||||
transformer: transformReactionRecord,
|
||||
tableName: REACTION,
|
||||
})) as ReactionModel[];
|
||||
batchRecords.push(...reactionsRecords);
|
||||
}
|
||||
|
||||
if (deleteReactions?.length && !skipSync) {
|
||||
deleteReactions.forEach((outCast) => outCast.prepareDestroyPermanently());
|
||||
batchRecords.push(...deleteReactions);
|
||||
}
|
||||
}
|
||||
|
||||
if (prepareRecordsOnly) {
|
||||
return batchRecords;
|
||||
}
|
||||
|
||||
if (batchRecords?.length) {
|
||||
await this.batchRecords(batchRecords, 'handleReactions');
|
||||
}
|
||||
|
||||
return batchRecords;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -99,7 +160,7 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
|
|||
* @param {(TransformerArgs) => Promise<Model>} execute.recordOperator
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async execute({createRaws, transformer, tableName, updateRaws}: OperationArgs): Promise<Model[]> {
|
||||
async execute<T extends Model>({createRaws, transformer, tableName, updateRaws}: OperationArgs<T>, description: string): Promise<T[]> {
|
||||
const models = await this.prepareRecords({
|
||||
tableName,
|
||||
createRaws,
|
||||
|
|
@ -108,7 +169,7 @@ export default class ServerDataOperatorBase extends BaseDataOperator {
|
|||
});
|
||||
|
||||
if (models?.length > 0) {
|
||||
await this.batchRecords(models);
|
||||
await this.batchRecords(models, description);
|
||||
}
|
||||
|
||||
return models;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ describe('*** Operator: Post Handlers tests ***', () => {
|
|||
createOrUpdateRawValues: drafts,
|
||||
tableName: 'Draft',
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
}, 'handleDraft');
|
||||
});
|
||||
|
||||
it('=> HandlePosts: should write to the Post and its sub-child tables', async () => {
|
||||
|
|
|
|||
|
|
@ -9,15 +9,19 @@ import {buildDraftKey} from '@database/operator/server_data_operator/comparators
|
|||
import {
|
||||
transformDraftRecord,
|
||||
transformFileRecord,
|
||||
transformPostInThreadRecord,
|
||||
transformPostRecord,
|
||||
transformPostsInChannelRecord,
|
||||
} from '@database/operator/server_data_operator/transformers/post';
|
||||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {createPostsChain} from '@database/operator/utils/post';
|
||||
import {getRawRecordPairs, getUniqueRawsBy, getValidRecordsForUpdate} from '@database/operator/utils/general';
|
||||
import {createPostsChain, getPostListEdges} from '@database/operator/utils/post';
|
||||
import {emptyFunction} from '@utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type Database from '@nozbe/watermelondb/Database';
|
||||
import type Model from '@nozbe/watermelondb/Model';
|
||||
import type {HandleDraftArgs, HandleFilesArgs, HandlePostsArgs, ProcessRecordResults} from '@typings/database/database';
|
||||
import type {HandleDraftArgs, HandleFilesArgs, HandlePostsArgs, RecordPair} from '@typings/database/database';
|
||||
import type DraftModel from '@typings/database/models/servers/draft';
|
||||
import type FileModel from '@typings/database/models/servers/file';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
|
|
@ -29,6 +33,8 @@ const {
|
|||
DRAFT,
|
||||
FILE,
|
||||
POST,
|
||||
POSTS_IN_CHANNEL,
|
||||
POSTS_IN_THREAD,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
type PostActionType = keyof typeof ActionType.POSTS;
|
||||
|
|
@ -39,9 +45,17 @@ export interface PostHandlerMix {
|
|||
handlePosts: ({actionType, order, posts, previousPostId, prepareRecordsOnly}: HandlePostsArgs) => Promise<Model[]>;
|
||||
handlePostsInChannel: (posts: Post[]) => Promise<void>;
|
||||
handlePostsInThread: (rootPosts: PostsInThread[]) => Promise<void>;
|
||||
|
||||
handleReceivedPostsInChannel: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostsInChannelSince: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostsInChannelBefore: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostsInChannelAfter: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostForChannel: (post: Post, prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
|
||||
handleReceivedPostsInThread: (postsMap: Record<string, Post[]>, prepareRecordsOnly?: boolean) => Promise<PostsInThreadModel[]>;
|
||||
}
|
||||
|
||||
const PostHandler = (superclass: any) => class extends superclass {
|
||||
const PostHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
/**
|
||||
* handleDraft: Handler responsible for the Create/Update operations occurring the Draft table from the 'Server' schema
|
||||
* @param {HandleDraftArgs} draftsArgs
|
||||
|
|
@ -66,7 +80,7 @@ const PostHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: DRAFT,
|
||||
});
|
||||
}, 'handleDraft');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -171,7 +185,7 @@ const PostHandler = (superclass: any) => class extends superclass {
|
|||
deleteRawValues: pendingPostsToDelete,
|
||||
tableName,
|
||||
fieldName: 'id',
|
||||
})) as ProcessRecordResults;
|
||||
}));
|
||||
|
||||
const preparedPosts = (await this.prepareRecords({
|
||||
createRaws: processedPosts.createRaws,
|
||||
|
|
@ -220,7 +234,7 @@ const PostHandler = (superclass: any) => class extends superclass {
|
|||
}
|
||||
|
||||
if (batch.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(batch);
|
||||
await this.batchRecords(batch, 'handlePosts');
|
||||
}
|
||||
|
||||
return batch;
|
||||
|
|
@ -245,7 +259,8 @@ const PostHandler = (superclass: any) => class extends superclass {
|
|||
createOrUpdateRawValues: files,
|
||||
tableName: FILE,
|
||||
fieldName: 'id',
|
||||
})) as ProcessRecordResults;
|
||||
deleteRawValues: [],
|
||||
}));
|
||||
|
||||
const postFiles = await this.prepareRecords({
|
||||
createRaws: processedFiles.createRaws,
|
||||
|
|
@ -259,7 +274,7 @@ const PostHandler = (superclass: any) => class extends superclass {
|
|||
}
|
||||
|
||||
if (postFiles?.length) {
|
||||
await this.batchRecords(postFiles);
|
||||
await this.batchRecords(postFiles, 'handleFiles');
|
||||
}
|
||||
|
||||
return postFiles;
|
||||
|
|
@ -327,6 +342,340 @@ const PostHandler = (superclass: any) => class extends superclass {
|
|||
|
||||
return [];
|
||||
};
|
||||
|
||||
// ========================
|
||||
// POST IN CHANNEL
|
||||
// ========================
|
||||
|
||||
_createPostsInChannelRecord = (channelId: string, earliest: number, latest: number, prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
// We should prepare instead of execute
|
||||
if (prepareRecordsOnly) {
|
||||
return this.prepareRecords({
|
||||
tableName: POSTS_IN_CHANNEL,
|
||||
createRaws: [{record: undefined, raw: {channel_id: channelId, earliest, latest}}],
|
||||
transformer: transformPostsInChannelRecord,
|
||||
});
|
||||
}
|
||||
return this.execute({
|
||||
createRaws: [{record: undefined, raw: {channel_id: channelId, earliest, latest}}],
|
||||
tableName: POSTS_IN_CHANNEL,
|
||||
transformer: transformPostsInChannelRecord,
|
||||
}, '_createPostsInChannelRecord');
|
||||
};
|
||||
|
||||
_mergePostInChannelChunks = async (newChunk: PostsInChannelModel, existingChunks: PostsInChannelModel[], prepareRecordsOnly = false) => {
|
||||
const result: PostsInChannelModel[] = [];
|
||||
for (const chunk of existingChunks) {
|
||||
if (newChunk.earliest <= chunk.earliest && newChunk.latest >= chunk.latest) {
|
||||
if (!prepareRecordsOnly) {
|
||||
newChunk.prepareUpdate(emptyFunction);
|
||||
}
|
||||
result.push(newChunk);
|
||||
result.push(chunk.prepareDestroyPermanently());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(result, '_mergePostInChannelChunks');
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannel = async (posts?: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannel method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost, lastPost} = getPostListEdges(posts);
|
||||
|
||||
// Channel Id for this chain of posts
|
||||
const channelId = firstPost.channel_id;
|
||||
|
||||
// Find smallest 'create_at' value in chain
|
||||
const earliest = firstPost.create_at;
|
||||
|
||||
// Find highest 'create_at' value in chain; -1 means we are dealing with one item in the posts array
|
||||
const latest = lastPost.create_at;
|
||||
|
||||
// Find the records in the PostsInChannel table that have a matching channel_id
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', channelId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
// chunk length 0; then it's a new chunk to be added to the PostsInChannel table
|
||||
if (chunks.length === 0) {
|
||||
return this._createPostsInChannelRecord(channelId, earliest, latest, prepareRecordsOnly);
|
||||
}
|
||||
|
||||
let targetChunk: PostsInChannelModel|undefined;
|
||||
|
||||
for (const chunk of chunks) {
|
||||
// find if we should plug the chain before
|
||||
if (firstPost.create_at >= chunk.earliest || latest <= chunk.latest) {
|
||||
targetChunk = chunk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetChunk) {
|
||||
if (
|
||||
targetChunk.earliest <= earliest &&
|
||||
targetChunk.latest >= latest
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If the chunk was found, Update the chunk and return
|
||||
if (prepareRecordsOnly) {
|
||||
targetChunk.prepareUpdate((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
return [targetChunk];
|
||||
}
|
||||
|
||||
targetChunk = await this.database.write(async () => {
|
||||
return targetChunk!.update((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
});
|
||||
|
||||
return [targetChunk!];
|
||||
}
|
||||
|
||||
// Create a new chunk and merge them if needed
|
||||
const newChunk = await this._createPostsInChannelRecord(channelId, earliest, latest, prepareRecordsOnly);
|
||||
const merged = await this._mergePostInChannelChunks(newChunk[0], chunks, prepareRecordsOnly);
|
||||
return merged;
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannelSince = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannelSince method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost} = getPostListEdges(posts);
|
||||
let latest = 0;
|
||||
|
||||
let recentChunk: PostsInChannelModel|undefined;
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', firstPost.channel_id),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
if (chunks.length) {
|
||||
recentChunk = chunks[0];
|
||||
|
||||
// add any new recent post while skipping the ones that were just updated
|
||||
for (const post of posts) {
|
||||
if (post.create_at > recentChunk.latest) {
|
||||
latest = post.create_at;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recentChunk && recentChunk.latest < latest) {
|
||||
// We've got new posts that belong to this chunk
|
||||
if (prepareRecordsOnly) {
|
||||
recentChunk.prepareUpdate((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
|
||||
return [recentChunk];
|
||||
}
|
||||
|
||||
recentChunk = await this.database.write(async () => {
|
||||
return recentChunk!.update((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
});
|
||||
|
||||
return [recentChunk!];
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannelBefore = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannelBefore method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost} = getPostListEdges(posts);
|
||||
|
||||
// Channel Id for this chain of posts
|
||||
const channelId = firstPost.channel_id;
|
||||
|
||||
// Find smallest 'create_at' value in chain
|
||||
const earliest = firstPost.create_at;
|
||||
|
||||
// Find the records in the PostsInChannel table that have a matching channel_id
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', channelId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
if (chunks.length === 0) {
|
||||
// No chunks found, previous posts in this block not found
|
||||
return [];
|
||||
}
|
||||
|
||||
let targetChunk = chunks[0];
|
||||
if (targetChunk) {
|
||||
if (targetChunk.earliest <= earliest) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If the chunk was found, Update the chunk and return
|
||||
if (prepareRecordsOnly) {
|
||||
targetChunk.prepareUpdate((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
});
|
||||
return [targetChunk];
|
||||
}
|
||||
|
||||
targetChunk = await this.database.write(async () => {
|
||||
return targetChunk!.update((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
});
|
||||
});
|
||||
|
||||
return [targetChunk!];
|
||||
}
|
||||
|
||||
return targetChunk;
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannelAfter = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
throw new Error(`handleReceivedPostsInChannelAfter Not implemented yet. posts count${posts.length} prepareRecordsOnly=${prepareRecordsOnly}`);
|
||||
};
|
||||
|
||||
handleReceivedPostForChannel = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostForChannel method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost, lastPost} = getPostListEdges(posts);
|
||||
|
||||
// Channel Id for this chain of posts
|
||||
const channelId = firstPost.channel_id;
|
||||
|
||||
// Find smallest 'create_at' value in chain
|
||||
const earliest = firstPost.create_at;
|
||||
|
||||
// Find highest 'create_at' value in chain; -1 means we are dealing with one item in the posts array
|
||||
const latest = lastPost.create_at;
|
||||
|
||||
// Find the records in the PostsInChannel table that have a matching channel_id
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', channelId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
// chunk length 0; then it's a new chunk to be added to the PostsInChannel table
|
||||
if (chunks.length === 0) {
|
||||
return this._createPostsInChannelRecord(channelId, earliest, latest, prepareRecordsOnly);
|
||||
}
|
||||
|
||||
let targetChunk = chunks[0];
|
||||
if (targetChunk) {
|
||||
if (targetChunk.latest >= latest) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If the chunk was found, Update the chunk and return
|
||||
if (prepareRecordsOnly) {
|
||||
targetChunk.prepareUpdate((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
return [targetChunk];
|
||||
}
|
||||
|
||||
targetChunk = await this.database.write(async () => {
|
||||
return targetChunk!.update((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
});
|
||||
|
||||
return [targetChunk!];
|
||||
}
|
||||
|
||||
return targetChunk;
|
||||
};
|
||||
|
||||
// ========================
|
||||
// POST IN THREAD
|
||||
// ========================
|
||||
|
||||
handleReceivedPostsInThread = async (postsMap: Record<string, Post[]>, prepareRecordsOnly = false): Promise<PostsInThreadModel[]> => {
|
||||
if (!postsMap || !Object.keys(postsMap).length) {
|
||||
logWarning(
|
||||
'An empty or undefined "postsMap" object has been passed to the handleReceivedPostsInThread method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const update: RecordPair[] = [];
|
||||
const create: PostsInThread[] = [];
|
||||
const ids = Object.keys(postsMap);
|
||||
for await (const rootId of ids) {
|
||||
const {firstPost, lastPost} = getPostListEdges(postsMap[rootId]);
|
||||
const chunks = (await this.database.get(POSTS_IN_THREAD).query(
|
||||
Q.where('root_id', rootId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInThreadModel[];
|
||||
|
||||
if (chunks.length) {
|
||||
const chunk = chunks[0];
|
||||
const newValue = {
|
||||
root_id: rootId,
|
||||
earliest: Math.min(chunk.earliest, firstPost.create_at),
|
||||
latest: Math.max(chunk.latest, lastPost.create_at),
|
||||
};
|
||||
update.push(getValidRecordsForUpdate({
|
||||
tableName: POSTS_IN_THREAD,
|
||||
newValue,
|
||||
existingRecord: chunk,
|
||||
}));
|
||||
} else {
|
||||
// create chunk
|
||||
create.push({
|
||||
root_id: rootId,
|
||||
earliest: firstPost.create_at,
|
||||
latest: lastPost.create_at,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const postInThreadRecords = (await this.prepareRecords({
|
||||
createRaws: getRawRecordPairs(create),
|
||||
updateRaws: update,
|
||||
transformer: transformPostInThreadRecord,
|
||||
tableName: POSTS_IN_THREAD,
|
||||
})) as PostsInThreadModel[];
|
||||
|
||||
if (postInThreadRecords?.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(postInThreadRecords, 'handleReceivedPostsInThread');
|
||||
}
|
||||
|
||||
return postInThreadRecords;
|
||||
};
|
||||
};
|
||||
|
||||
export default PostHandler;
|
||||
|
|
|
|||
|
|
@ -1,298 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
|
||||
import {Database} from '@constants';
|
||||
import {getPostListEdges} from '@database//operator/utils/post';
|
||||
import {transformPostsInChannelRecord} from '@database/operator/server_data_operator/transformers/post';
|
||||
import {emptyFunction} from '@utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type PostsInChannelModel from '@typings/database/models/servers/posts_in_channel';
|
||||
|
||||
export interface PostsInChannelHandlerMix {
|
||||
handleReceivedPostsInChannel: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostsInChannelSince: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostsInChannelBefore: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostsInChannelAfter: (posts: Post[], prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
handleReceivedPostForChannel: (post: Post, prepareRecordsOnly?: boolean) => Promise<PostsInChannelModel[]>;
|
||||
}
|
||||
|
||||
const {POSTS_IN_CHANNEL} = Database.MM_TABLES.SERVER;
|
||||
|
||||
const PostsInChannelHandler = (superclass: any) => class extends superclass {
|
||||
_createPostsInChannelRecord = (channelId: string, earliest: number, latest: number, prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
// We should prepare instead of execute
|
||||
if (prepareRecordsOnly) {
|
||||
return this.prepareRecords({
|
||||
tableName: POSTS_IN_CHANNEL,
|
||||
createRaws: [{record: undefined, raw: {channel_id: channelId, earliest, latest}}],
|
||||
transformer: transformPostsInChannelRecord,
|
||||
});
|
||||
}
|
||||
return this.execute({
|
||||
createRaws: [{record: undefined, raw: {channel_id: channelId, earliest, latest}}],
|
||||
tableName: POSTS_IN_CHANNEL,
|
||||
transformer: transformPostsInChannelRecord,
|
||||
});
|
||||
};
|
||||
|
||||
_mergePostInChannelChunks = async (newChunk: PostsInChannelModel, existingChunks: PostsInChannelModel[], prepareRecordsOnly = false) => {
|
||||
const result: PostsInChannelModel[] = [];
|
||||
for (const chunk of existingChunks) {
|
||||
if (newChunk.earliest <= chunk.earliest && newChunk.latest >= chunk.latest) {
|
||||
if (!prepareRecordsOnly) {
|
||||
newChunk.prepareUpdate(emptyFunction);
|
||||
}
|
||||
result.push(newChunk);
|
||||
result.push(chunk.prepareDestroyPermanently());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannel = async (posts?: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannel method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost, lastPost} = getPostListEdges(posts);
|
||||
|
||||
// Channel Id for this chain of posts
|
||||
const channelId = firstPost.channel_id;
|
||||
|
||||
// Find smallest 'create_at' value in chain
|
||||
const earliest = firstPost.create_at;
|
||||
|
||||
// Find highest 'create_at' value in chain; -1 means we are dealing with one item in the posts array
|
||||
const latest = lastPost.create_at;
|
||||
|
||||
// Find the records in the PostsInChannel table that have a matching channel_id
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', channelId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
// chunk length 0; then it's a new chunk to be added to the PostsInChannel table
|
||||
if (chunks.length === 0) {
|
||||
return this._createPostsInChannelRecord(channelId, earliest, latest, prepareRecordsOnly);
|
||||
}
|
||||
|
||||
let targetChunk: PostsInChannelModel|undefined;
|
||||
|
||||
for (const chunk of chunks) {
|
||||
// find if we should plug the chain before
|
||||
if (firstPost.create_at >= chunk.earliest || latest <= chunk.latest) {
|
||||
targetChunk = chunk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetChunk) {
|
||||
if (
|
||||
targetChunk.earliest <= earliest &&
|
||||
targetChunk.latest >= latest
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If the chunk was found, Update the chunk and return
|
||||
if (prepareRecordsOnly) {
|
||||
targetChunk.prepareUpdate((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
return [targetChunk];
|
||||
}
|
||||
|
||||
targetChunk = await this.database.write(async () => {
|
||||
return targetChunk!.update((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
});
|
||||
|
||||
return [targetChunk!];
|
||||
}
|
||||
|
||||
// Create a new chunk and merge them if needed
|
||||
const newChunk = await this._createPostsInChannelRecord(channelId, earliest, latest, prepareRecordsOnly);
|
||||
const merged = await this._mergePostInChannelChunks(newChunk[0], chunks, prepareRecordsOnly);
|
||||
return merged;
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannelSince = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannelSince method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost} = getPostListEdges(posts);
|
||||
let latest = 0;
|
||||
|
||||
let recentChunk: PostsInChannelModel|undefined;
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', firstPost.channel_id),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
if (chunks.length) {
|
||||
recentChunk = chunks[0];
|
||||
|
||||
// add any new recent post while skipping the ones that were just updated
|
||||
for (const post of posts) {
|
||||
if (post.create_at > recentChunk.latest) {
|
||||
latest = post.create_at;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (recentChunk && recentChunk.latest < latest) {
|
||||
// We've got new posts that belong to this chunk
|
||||
if (prepareRecordsOnly) {
|
||||
recentChunk.prepareUpdate((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
|
||||
return [recentChunk];
|
||||
}
|
||||
|
||||
recentChunk = await this.database.write(async () => {
|
||||
return recentChunk!.update((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
});
|
||||
|
||||
return [recentChunk!];
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannelBefore = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostsInChannelBefore method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost} = getPostListEdges(posts);
|
||||
|
||||
// Channel Id for this chain of posts
|
||||
const channelId = firstPost.channel_id;
|
||||
|
||||
// Find smallest 'create_at' value in chain
|
||||
const earliest = firstPost.create_at;
|
||||
|
||||
// Find the records in the PostsInChannel table that have a matching channel_id
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', channelId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
if (chunks.length === 0) {
|
||||
// No chunks found, previous posts in this block not found
|
||||
return [];
|
||||
}
|
||||
|
||||
let targetChunk = chunks[0];
|
||||
if (targetChunk) {
|
||||
if (targetChunk.earliest <= earliest) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If the chunk was found, Update the chunk and return
|
||||
if (prepareRecordsOnly) {
|
||||
targetChunk.prepareUpdate((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
});
|
||||
return [targetChunk];
|
||||
}
|
||||
|
||||
targetChunk = await this.database.write(async () => {
|
||||
return targetChunk!.update((record) => {
|
||||
record.earliest = Math.min(record.earliest, earliest);
|
||||
});
|
||||
});
|
||||
|
||||
return [targetChunk!];
|
||||
}
|
||||
|
||||
return targetChunk;
|
||||
};
|
||||
|
||||
handleReceivedPostsInChannelAfter = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
throw new Error(`handleReceivedPostsInChannelAfter Not implemented yet. posts count${posts.length} prepareRecordsOnly=${prepareRecordsOnly}`);
|
||||
};
|
||||
|
||||
handleReceivedPostForChannel = async (posts: Post[], prepareRecordsOnly = false): Promise<PostsInChannelModel[]> => {
|
||||
if (!posts?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "posts" array has been passed to the handleReceivedPostForChannel method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const {firstPost, lastPost} = getPostListEdges(posts);
|
||||
|
||||
// Channel Id for this chain of posts
|
||||
const channelId = firstPost.channel_id;
|
||||
|
||||
// Find smallest 'create_at' value in chain
|
||||
const earliest = firstPost.create_at;
|
||||
|
||||
// Find highest 'create_at' value in chain; -1 means we are dealing with one item in the posts array
|
||||
const latest = lastPost.create_at;
|
||||
|
||||
// Find the records in the PostsInChannel table that have a matching channel_id
|
||||
const chunks = (await this.database.get(POSTS_IN_CHANNEL).query(
|
||||
Q.where('channel_id', channelId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInChannelModel[];
|
||||
|
||||
// chunk length 0; then it's a new chunk to be added to the PostsInChannel table
|
||||
if (chunks.length === 0) {
|
||||
return this._createPostsInChannelRecord(channelId, earliest, latest, prepareRecordsOnly);
|
||||
}
|
||||
|
||||
let targetChunk = chunks[0];
|
||||
if (targetChunk) {
|
||||
if (targetChunk.latest >= latest) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// If the chunk was found, Update the chunk and return
|
||||
if (prepareRecordsOnly) {
|
||||
targetChunk.prepareUpdate((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
return [targetChunk];
|
||||
}
|
||||
|
||||
targetChunk = await this.database.write(async () => {
|
||||
return targetChunk!.update((record) => {
|
||||
record.latest = Math.max(record.latest, latest);
|
||||
});
|
||||
});
|
||||
|
||||
return [targetChunk!];
|
||||
}
|
||||
|
||||
return targetChunk;
|
||||
};
|
||||
};
|
||||
|
||||
export default PostsInChannelHandler;
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Q} from '@nozbe/watermelondb';
|
||||
|
||||
import {Database} from '@constants';
|
||||
import {getPostListEdges} from '@database//operator/utils/post';
|
||||
import {transformPostInThreadRecord} from '@database/operator/server_data_operator/transformers/post';
|
||||
import {getRawRecordPairs, getValidRecordsForUpdate} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type {RecordPair} from '@typings/database/database';
|
||||
import type PostsInThreadModel from '@typings/database/models/servers/posts_in_thread';
|
||||
|
||||
export interface PostsInThreadHandlerMix {
|
||||
handleReceivedPostsInThread: (postsMap: Record<string, Post[]>, prepareRecordsOnly?: boolean) => Promise<PostsInThreadModel[]>;
|
||||
}
|
||||
|
||||
const {POSTS_IN_THREAD} = Database.MM_TABLES.SERVER;
|
||||
|
||||
const PostsInThreadHandler = (superclass: any) => class extends superclass {
|
||||
handleReceivedPostsInThread = async (postsMap: Record<string, Post[]>, prepareRecordsOnly = false): Promise<PostsInThreadModel[]> => {
|
||||
if (!postsMap || !Object.keys(postsMap).length) {
|
||||
logWarning(
|
||||
'An empty or undefined "postsMap" object has been passed to the handleReceivedPostsInThread method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const update: RecordPair[] = [];
|
||||
const create: PostsInThread[] = [];
|
||||
const ids = Object.keys(postsMap);
|
||||
for await (const rootId of ids) {
|
||||
const {firstPost, lastPost} = getPostListEdges(postsMap[rootId]);
|
||||
const chunks = (await this.database.get(POSTS_IN_THREAD).query(
|
||||
Q.where('root_id', rootId),
|
||||
Q.sortBy('latest', Q.desc),
|
||||
).fetch()) as PostsInThreadModel[];
|
||||
|
||||
if (chunks.length) {
|
||||
const chunk = chunks[0];
|
||||
const newValue = {
|
||||
root_id: rootId,
|
||||
earliest: Math.min(chunk.earliest, firstPost.create_at),
|
||||
latest: Math.max(chunk.latest, lastPost.create_at),
|
||||
};
|
||||
update.push(getValidRecordsForUpdate({
|
||||
tableName: POSTS_IN_THREAD,
|
||||
newValue,
|
||||
existingRecord: chunk,
|
||||
}));
|
||||
} else {
|
||||
// create chunk
|
||||
create.push({
|
||||
root_id: rootId,
|
||||
earliest: firstPost.create_at,
|
||||
latest: lastPost.create_at,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const postInThreadRecords = (await this.prepareRecords({
|
||||
createRaws: getRawRecordPairs(create),
|
||||
updateRaws: update,
|
||||
transformer: transformPostInThreadRecord,
|
||||
tableName: POSTS_IN_THREAD,
|
||||
})) as PostsInThreadModel[];
|
||||
|
||||
if (postInThreadRecords?.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(postInThreadRecords);
|
||||
}
|
||||
|
||||
return postInThreadRecords;
|
||||
};
|
||||
};
|
||||
|
||||
export default PostsInThreadHandler;
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {transformReactionRecord} from '@database/operator/server_data_operator/transformers/reaction';
|
||||
import {sanitizeReactions} from '@database/operator/utils/reaction';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type {HandleReactionsArgs} from '@typings/database/database';
|
||||
import type CustomEmojiModel from '@typings/database/models/servers/custom_emoji';
|
||||
import type ReactionModel from '@typings/database/models/servers/reaction';
|
||||
|
||||
const {REACTION} = MM_TABLES.SERVER;
|
||||
|
||||
export interface ReactionHandlerMix {
|
||||
handleReactions: ({postsReactions, prepareRecordsOnly}: HandleReactionsArgs) => Promise<Array<ReactionModel | CustomEmojiModel>>;
|
||||
}
|
||||
|
||||
const ReactionHandler = (superclass: any) => class extends superclass {
|
||||
/**
|
||||
* handleReactions: Handler responsible for the Create/Update operations occurring on the Reaction table from the 'Server' schema
|
||||
* @param {HandleReactionsArgs} handleReactions
|
||||
* @param {ReactionsPerPost[]} handleReactions.postsReactions
|
||||
* @param {boolean} handleReactions.prepareRecordsOnly
|
||||
* @param {boolean} handleReactions.skipSync
|
||||
* @returns {Promise<Array<(ReactionModel | CustomEmojiModel)>>}
|
||||
*/
|
||||
handleReactions = async ({postsReactions, prepareRecordsOnly, skipSync}: HandleReactionsArgs): Promise<ReactionModel[]> => {
|
||||
const batchRecords: ReactionModel[] = [];
|
||||
|
||||
if (!postsReactions?.length) {
|
||||
logWarning(
|
||||
'An empty or undefined "postsReactions" array has been passed to the handleReactions method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
for await (const postReactions of postsReactions) {
|
||||
const {post_id, reactions} = postReactions;
|
||||
const {
|
||||
createReactions,
|
||||
deleteReactions,
|
||||
} = await sanitizeReactions({
|
||||
database: this.database,
|
||||
post_id,
|
||||
rawReactions: reactions,
|
||||
skipSync,
|
||||
});
|
||||
|
||||
if (createReactions?.length) {
|
||||
// Prepares record for model Reactions
|
||||
const reactionsRecords = (await this.prepareRecords({
|
||||
createRaws: createReactions,
|
||||
transformer: transformReactionRecord,
|
||||
tableName: REACTION,
|
||||
})) as ReactionModel[];
|
||||
batchRecords.push(...reactionsRecords);
|
||||
}
|
||||
|
||||
if (deleteReactions?.length && !skipSync) {
|
||||
deleteReactions.forEach((outCast) => outCast.prepareDestroyPermanently());
|
||||
batchRecords.push(...deleteReactions);
|
||||
}
|
||||
}
|
||||
|
||||
if (prepareRecordsOnly) {
|
||||
return batchRecords;
|
||||
}
|
||||
|
||||
if (batchRecords?.length) {
|
||||
await this.batchRecords(batchRecords);
|
||||
}
|
||||
|
||||
return batchRecords;
|
||||
};
|
||||
};
|
||||
|
||||
export default ReactionHandler;
|
||||
|
|
@ -60,7 +60,7 @@ describe('*** Operator: Team Handlers tests ***', () => {
|
|||
tableName: 'Team',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformTeamRecord,
|
||||
});
|
||||
}, 'handleTeam');
|
||||
});
|
||||
|
||||
it('=> HandleTeamMemberships: should write to the TEAM_MEMBERSHIP table', async () => {
|
||||
|
|
@ -98,7 +98,7 @@ describe('*** Operator: Team Handlers tests ***', () => {
|
|||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildTeamMembershipKey,
|
||||
transformer: transformTeamMembershipRecord,
|
||||
});
|
||||
}, 'handleTeamMemberships');
|
||||
});
|
||||
|
||||
it('=> HandleMyTeam: should write to the MY_TEAM table', async () => {
|
||||
|
|
@ -124,7 +124,7 @@ describe('*** Operator: Team Handlers tests ***', () => {
|
|||
tableName: 'MyTeam',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformMyTeamRecord,
|
||||
});
|
||||
}, 'handleMyTeam');
|
||||
});
|
||||
|
||||
it('=> HandleTeamChannelHistory: should write to the TEAM_CHANNEL_HISTORY table', async () => {
|
||||
|
|
@ -150,7 +150,7 @@ describe('*** Operator: Team Handlers tests ***', () => {
|
|||
tableName: 'TeamChannelHistory',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformTeamChannelHistoryRecord,
|
||||
});
|
||||
}, 'handleTeamChannelHistory');
|
||||
});
|
||||
|
||||
it('=> HandleTeamSearchHistory: should write to the TEAM_SEARCH_HISTORY table', async () => {
|
||||
|
|
@ -179,6 +179,6 @@ describe('*** Operator: Team Handlers tests ***', () => {
|
|||
prepareRecordsOnly: false,
|
||||
buildKeyRecordBy: buildTeamSearchHistoryKey,
|
||||
transformer: transformTeamSearchHistoryRecord,
|
||||
});
|
||||
}, 'handleTeamSearchHistory');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {
|
||||
HandleMyTeamArgs, HandleTeamArgs,
|
||||
HandleTeamChannelHistoryArgs, HandleTeamMembershipArgs, HandleTeamSearchHistoryArgs,
|
||||
|
|
@ -44,7 +45,7 @@ export interface TeamHandlerMix {
|
|||
handleMyTeam: ({myTeams, prepareRecordsOnly}: HandleMyTeamArgs) => Promise<MyTeamModel[]>;
|
||||
}
|
||||
|
||||
const TeamHandler = (superclass: any) => class extends superclass {
|
||||
const TeamHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
/**
|
||||
* handleTeamMemberships: Handler responsible for the Create/Update operations occurring on the TEAM_MEMBERSHIP table from the 'Server' schema
|
||||
* @param {HandleTeamMembershipArgs} teamMembershipsArgs
|
||||
|
|
@ -97,7 +98,7 @@ const TeamHandler = (superclass: any) => class extends superclass {
|
|||
createOrUpdateRawValues,
|
||||
tableName: TEAM_MEMBERSHIP,
|
||||
prepareRecordsOnly,
|
||||
});
|
||||
}, 'handleTeamMemberships');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -146,7 +147,7 @@ const TeamHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: TEAM,
|
||||
});
|
||||
}, 'handleTeam');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -172,7 +173,7 @@ const TeamHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: TEAM_CHANNEL_HISTORY,
|
||||
});
|
||||
}, 'handleTeamChannelHistory');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -199,7 +200,7 @@ const TeamHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: TEAM_SEARCH_HISTORY,
|
||||
});
|
||||
}, 'handleTeamSearchHistory');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -248,7 +249,7 @@ const TeamHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly,
|
||||
createOrUpdateRawValues,
|
||||
tableName: MY_TEAM,
|
||||
});
|
||||
}, 'handleMyTeam');
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {transformTeamThreadsSyncRecord} from '@database/operator/server_data_ope
|
|||
import {getRawRecordPairs, getUniqueRawsBy, getValidRecordsForUpdate} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {HandleTeamThreadsSyncArgs, RecordPair} from '@typings/database/database';
|
||||
import type TeamThreadsSyncModel from '@typings/database/models/servers/team_threads_sync';
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ export interface TeamThreadsSyncHandlerMix {
|
|||
|
||||
const {TEAM_THREADS_SYNC} = MM_TABLES.SERVER;
|
||||
|
||||
const TeamThreadsSyncHandler = (superclass: any) => class extends superclass {
|
||||
const TeamThreadsSyncHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
handleTeamThreadsSync = async ({data, prepareRecordsOnly = false}: HandleTeamThreadsSyncArgs): Promise<TeamThreadsSyncModel[]> => {
|
||||
if (!data || !data.length) {
|
||||
logWarning(
|
||||
|
|
@ -61,7 +62,7 @@ const TeamThreadsSyncHandler = (superclass: any) => class extends superclass {
|
|||
})) as TeamThreadsSyncModel[];
|
||||
|
||||
if (models?.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(models);
|
||||
await this.batchRecords(models, 'handleTeamThreadsSync');
|
||||
}
|
||||
|
||||
return models;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
|||
createOrUpdateRawValues: threads,
|
||||
tableName: 'Thread',
|
||||
prepareRecordsOnly: true,
|
||||
});
|
||||
}, 'handleThreads(NEVER)');
|
||||
|
||||
// Should handle participants
|
||||
expect(spyOnHandleThreadParticipants).toHaveBeenCalledWith({
|
||||
|
|
|
|||
|
|
@ -7,14 +7,16 @@ import {MM_TABLES} from '@constants/database';
|
|||
import {
|
||||
transformThreadRecord,
|
||||
transformThreadParticipantRecord,
|
||||
transformThreadInTeamRecord,
|
||||
} from '@database/operator/server_data_operator/transformers/thread';
|
||||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {getRawRecordPairs, getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
import {sanitizeThreadParticipants} from '@database/operator/utils/thread';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type Database from '@nozbe/watermelondb/Database';
|
||||
import type Model from '@nozbe/watermelondb/Model';
|
||||
import type {HandleThreadsArgs, HandleThreadParticipantsArgs} from '@typings/database/database';
|
||||
import type {HandleThreadsArgs, HandleThreadParticipantsArgs, HandleThreadInTeamArgs} from '@typings/database/database';
|
||||
import type ThreadModel from '@typings/database/models/servers/thread';
|
||||
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
|
||||
import type ThreadParticipantModel from '@typings/database/models/servers/thread_participant';
|
||||
|
|
@ -22,14 +24,16 @@ import type ThreadParticipantModel from '@typings/database/models/servers/thread
|
|||
const {
|
||||
THREAD,
|
||||
THREAD_PARTICIPANT,
|
||||
THREADS_IN_TEAM,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
export interface ThreadHandlerMix {
|
||||
handleThreads: ({threads, teamId, prepareRecordsOnly}: HandleThreadsArgs) => Promise<Model[]>;
|
||||
handleThreadParticipants: ({threadsParticipants, prepareRecordsOnly}: HandleThreadParticipantsArgs) => Promise<ThreadParticipantModel[]>;
|
||||
handleThreadInTeam: ({threadsMap, prepareRecordsOnly}: HandleThreadInTeamArgs) => Promise<ThreadInTeamModel[]>;
|
||||
}
|
||||
|
||||
const ThreadHandler = (superclass: any) => class extends superclass {
|
||||
const ThreadHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
/**
|
||||
* handleThreads: Handler responsible for the Create/Update operations occurring on the Thread table from the 'Server' schema
|
||||
* @param {HandleThreadsArgs} handleThreads
|
||||
|
|
@ -49,11 +53,11 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
|||
const uniqueThreads = getUniqueRawsBy({
|
||||
raws: threads,
|
||||
key: 'id',
|
||||
}) as Thread[];
|
||||
}) as ThreadWithLastFetchedAt[];
|
||||
|
||||
// Seperate threads to be deleted & created/updated
|
||||
const deletedThreadIds: string[] = [];
|
||||
const createOrUpdateThreads: Thread[] = [];
|
||||
const createOrUpdateThreads: ThreadWithLastFetchedAt[] = [];
|
||||
uniqueThreads.forEach((thread) => {
|
||||
if (thread.delete_at > 0) {
|
||||
deletedThreadIds.push(thread.id);
|
||||
|
|
@ -106,7 +110,7 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly: true,
|
||||
createOrUpdateRawValues: createOrUpdateThreads,
|
||||
tableName: THREAD,
|
||||
}) as ThreadModel[];
|
||||
}, 'handleThreads(NEVER)');
|
||||
|
||||
// Add the models to be batched here
|
||||
const batch: Model[] = [...preparedThreads];
|
||||
|
|
@ -124,7 +128,7 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
|||
}
|
||||
|
||||
if (batch.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(batch);
|
||||
await this.batchRecords(batch, 'handleThreads');
|
||||
}
|
||||
|
||||
return batch;
|
||||
|
|
@ -175,11 +179,58 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
|||
}
|
||||
|
||||
if (batchRecords?.length) {
|
||||
await this.batchRecords(batchRecords);
|
||||
await this.batchRecords(batchRecords, 'handleThreadParticipants');
|
||||
}
|
||||
|
||||
return batchRecords;
|
||||
};
|
||||
|
||||
handleThreadInTeam = async ({threadsMap, prepareRecordsOnly = false}: HandleThreadInTeamArgs): Promise<ThreadInTeamModel[]> => {
|
||||
if (!threadsMap || !Object.keys(threadsMap).length) {
|
||||
logWarning(
|
||||
'An empty or undefined "threadsMap" object has been passed to the handleReceivedPostForChannel method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const create: ThreadInTeam[] = [];
|
||||
const teamIds = Object.keys(threadsMap);
|
||||
for await (const teamId of teamIds) {
|
||||
const threadIds = threadsMap[teamId].map((thread) => thread.id);
|
||||
const chunks = await (this.database as Database).get<ThreadInTeamModel>(THREADS_IN_TEAM).query(
|
||||
Q.where('team_id', teamId),
|
||||
Q.where('id', Q.oneOf(threadIds)),
|
||||
).fetch();
|
||||
const chunksMap = chunks.reduce((result: Record<string, ThreadInTeamModel>, chunk) => {
|
||||
result[chunk.threadId] = chunk;
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
for (const thread of threadsMap[teamId]) {
|
||||
const chunk = chunksMap[thread.id];
|
||||
|
||||
// Create if the chunk is not found
|
||||
if (!chunk) {
|
||||
create.push({
|
||||
thread_id: thread.id,
|
||||
team_id: teamId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const threadsInTeam = (await this.prepareRecords({
|
||||
createRaws: getRawRecordPairs(create),
|
||||
transformer: transformThreadInTeamRecord,
|
||||
tableName: THREADS_IN_TEAM,
|
||||
})) as ThreadInTeamModel[];
|
||||
|
||||
if (threadsInTeam?.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(threadsInTeam, 'handleThreadInTeam');
|
||||
}
|
||||
|
||||
return threadsInTeam;
|
||||
};
|
||||
};
|
||||
|
||||
export default ThreadHandler;
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Q, Database} from '@nozbe/watermelondb';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {transformThreadInTeamRecord} from '@database/operator/server_data_operator/transformers/thread';
|
||||
import {getRawRecordPairs} from '@database/operator/utils/general';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type {HandleThreadInTeamArgs} from '@typings/database/database';
|
||||
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
|
||||
|
||||
export interface ThreadInTeamHandlerMix {
|
||||
handleThreadInTeam: ({threadsMap, prepareRecordsOnly}: HandleThreadInTeamArgs) => Promise<ThreadInTeamModel[]>;
|
||||
}
|
||||
|
||||
const {THREADS_IN_TEAM} = MM_TABLES.SERVER;
|
||||
|
||||
const ThreadInTeamHandler = (superclass: any) => class extends superclass {
|
||||
handleThreadInTeam = async ({threadsMap, prepareRecordsOnly = false}: HandleThreadInTeamArgs): Promise<ThreadInTeamModel[]> => {
|
||||
if (!threadsMap || !Object.keys(threadsMap).length) {
|
||||
logWarning(
|
||||
'An empty or undefined "threadsMap" object has been passed to the handleReceivedPostForChannel method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const create: ThreadInTeam[] = [];
|
||||
const teamIds = Object.keys(threadsMap);
|
||||
for await (const teamId of teamIds) {
|
||||
const threadIds = threadsMap[teamId].map((thread) => thread.id);
|
||||
const chunks = await (this.database as Database).get<ThreadInTeamModel>(THREADS_IN_TEAM).query(
|
||||
Q.where('team_id', teamId),
|
||||
Q.where('id', Q.oneOf(threadIds)),
|
||||
).fetch();
|
||||
const chunksMap = chunks.reduce((result: Record<string, ThreadInTeamModel>, chunk) => {
|
||||
result[chunk.threadId] = chunk;
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
for (const thread of threadsMap[teamId]) {
|
||||
const chunk = chunksMap[thread.id];
|
||||
|
||||
// Create if the chunk is not found
|
||||
if (!chunk) {
|
||||
create.push({
|
||||
thread_id: thread.id,
|
||||
team_id: teamId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const threadsInTeam = (await this.prepareRecords({
|
||||
createRaws: getRawRecordPairs(create),
|
||||
transformer: transformThreadInTeamRecord,
|
||||
tableName: THREADS_IN_TEAM,
|
||||
})) as ThreadInTeamModel[];
|
||||
|
||||
if (threadsInTeam?.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(threadsInTeam);
|
||||
}
|
||||
|
||||
return threadsInTeam;
|
||||
};
|
||||
};
|
||||
|
||||
export default ThreadInTeamHandler;
|
||||
|
|
@ -102,7 +102,7 @@ describe('*** Operator: User Handlers tests ***', () => {
|
|||
tableName: 'User',
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformUserRecord,
|
||||
});
|
||||
}, 'handleUsers');
|
||||
});
|
||||
|
||||
it('=> HandlePreferences: should write to the PREFERENCE table', async () => {
|
||||
|
|
@ -151,6 +151,6 @@ describe('*** Operator: User Handlers tests ***', () => {
|
|||
prepareRecordsOnly: true,
|
||||
buildKeyRecordBy: buildPreferenceKey,
|
||||
transformer: transformPreferenceRecord,
|
||||
});
|
||||
}, 'handlePreferences(NEVER)');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {getUniqueRawsBy} from '@database/operator/utils/general';
|
|||
import {filterPreferences} from '@helpers/api/preference';
|
||||
import {logWarning} from '@utils/log';
|
||||
|
||||
import type ServerDataOperatorBase from '.';
|
||||
import type {
|
||||
HandlePreferencesArgs,
|
||||
HandleUsersArgs,
|
||||
|
|
@ -25,7 +26,7 @@ export interface UserHandlerMix {
|
|||
handleUsers: ({users, prepareRecordsOnly}: HandleUsersArgs) => Promise<UserModel[]>;
|
||||
}
|
||||
|
||||
const UserHandler = (superclass: any) => class extends superclass {
|
||||
const UserHandler = <TBase extends Constructor<ServerDataOperatorBase>>(superclass: TBase) => class extends superclass {
|
||||
/**
|
||||
* handlePreferences: Handler responsible for the Create/Update operations occurring on the PREFERENCE table from the 'Server' schema
|
||||
* @param {HandlePreferencesArgs} preferencesArgs
|
||||
|
|
@ -87,7 +88,7 @@ const UserHandler = (superclass: any) => class extends superclass {
|
|||
prepareRecordsOnly: true,
|
||||
createOrUpdateRawValues,
|
||||
tableName: PREFERENCE,
|
||||
});
|
||||
}, 'handlePreferences(NEVER)');
|
||||
records.push(...createOrUpdate);
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +97,7 @@ const UserHandler = (superclass: any) => class extends superclass {
|
|||
}
|
||||
|
||||
if (records.length && !prepareRecordsOnly) {
|
||||
await this.batchRecords(records);
|
||||
await this.batchRecords(records, 'handlePreferences');
|
||||
}
|
||||
|
||||
return records;
|
||||
|
|
@ -125,7 +126,7 @@ const UserHandler = (superclass: any) => class extends superclass {
|
|||
createOrUpdateRawValues,
|
||||
tableName: USER,
|
||||
prepareRecordsOnly,
|
||||
});
|
||||
}, 'handleUsers');
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,9 @@ import CategoryHandler, {CategoryHandlerMix} from '@database/operator/server_dat
|
|||
import ChannelHandler, {ChannelHandlerMix} from '@database/operator/server_data_operator/handlers/channel';
|
||||
import GroupHandler, {GroupHandlerMix} from '@database/operator/server_data_operator/handlers/group';
|
||||
import PostHandler, {PostHandlerMix} from '@database/operator/server_data_operator/handlers/post';
|
||||
import PostsInChannelHandler, {PostsInChannelHandlerMix} from '@database/operator/server_data_operator/handlers/posts_in_channel';
|
||||
import PostsInThreadHandler, {PostsInThreadHandlerMix} from '@database/operator/server_data_operator/handlers/posts_in_thread';
|
||||
import ReactionHander, {ReactionHandlerMix} from '@database/operator/server_data_operator/handlers/reaction';
|
||||
import TeamHandler, {TeamHandlerMix} from '@database/operator/server_data_operator/handlers/team';
|
||||
import TeamThreadsSyncHandler, {TeamThreadsSyncHandlerMix} from '@database/operator/server_data_operator/handlers/team_threads_sync';
|
||||
import ThreadHandler, {ThreadHandlerMix} from '@database/operator/server_data_operator/handlers/thread';
|
||||
import ThreadInTeamHandler, {ThreadInTeamHandlerMix} from '@database/operator/server_data_operator/handlers/thread_in_team';
|
||||
import UserHandler, {UserHandlerMix} from '@database/operator/server_data_operator/handlers/user';
|
||||
import mix from '@utils/mix';
|
||||
|
||||
|
|
@ -23,13 +19,9 @@ interface ServerDataOperator extends
|
|||
ChannelHandlerMix,
|
||||
GroupHandlerMix,
|
||||
PostHandlerMix,
|
||||
PostsInChannelHandlerMix,
|
||||
PostsInThreadHandlerMix,
|
||||
ReactionHandlerMix,
|
||||
ServerDataOperatorBase,
|
||||
TeamHandlerMix,
|
||||
ThreadHandlerMix,
|
||||
ThreadInTeamHandlerMix,
|
||||
TeamThreadsSyncHandlerMix,
|
||||
UserHandlerMix
|
||||
{}
|
||||
|
|
@ -39,12 +31,8 @@ class ServerDataOperator extends mix(ServerDataOperatorBase).with(
|
|||
ChannelHandler,
|
||||
GroupHandler,
|
||||
PostHandler,
|
||||
PostsInChannelHandler,
|
||||
PostsInThreadHandler,
|
||||
ReactionHander,
|
||||
TeamHandler,
|
||||
ThreadHandler,
|
||||
ThreadInTeamHandler,
|
||||
TeamThreadsSyncHandler,
|
||||
UserHandler,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import type {Model} from '@nozbe/watermelondb';
|
||||
import type {IdenticalRecordArgs, RangeOfValueArgs, RecordPair, RetrieveRecordsArgs} from '@typings/database/database';
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
|
|
@ -92,6 +93,6 @@ export const getUniqueRawsBy = ({raws, key}: { raws: RawValue[]; key: string}) =
|
|||
* @param {Clause} records.condition
|
||||
* @returns {Promise<Model[]>}
|
||||
*/
|
||||
export const retrieveRecords = ({database, tableName, condition}: RetrieveRecordsArgs) => {
|
||||
return database.collections.get(tableName).query(condition).fetch();
|
||||
export const retrieveRecords = <T extends Model>({database, tableName, condition}: RetrieveRecordsArgs) => {
|
||||
return database.collections.get<T>(tableName).query(condition).fetch();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -84,13 +84,13 @@ describe('draft upload manager', () => {
|
|||
|
||||
const fileClientId = 'clientId';
|
||||
const fileServerId = 'serverId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId: fileClientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId: fileClientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
manager.prepareUpload(url, {clientId: fileClientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId: fileClientId, localPath: 'path1'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(fileClientId)).toBe(true);
|
||||
|
||||
expect(uploadMocks.resolvePromise).not.toBeNull();
|
||||
uploadMocks.resolvePromise!({ok: true, code: 201, data: {file_infos: [{clientId: fileClientId, id: fileServerId}]}});
|
||||
uploadMocks.resolvePromise!({ok: true, code: 201, data: {file_infos: [{clientId: fileClientId, id: fileServerId, localPath: 'path1'}]}});
|
||||
|
||||
// Wait for other promises (on complete write) to finish
|
||||
await new Promise(process.nextTick);
|
||||
|
|
@ -107,9 +107,9 @@ describe('draft upload manager', () => {
|
|||
const uploadMocks = mockUpload();
|
||||
|
||||
const fileClientId = 'clientId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId: fileClientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId: fileClientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
manager.prepareUpload(url, {clientId: fileClientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId: fileClientId, localPath: 'path2'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(fileClientId)).toBe(true);
|
||||
|
||||
// Wait for other promises to finish
|
||||
|
|
@ -260,13 +260,13 @@ describe('draft upload manager', () => {
|
|||
|
||||
const fileClientId = 'clientId';
|
||||
const fileServerId = 'serverId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId: fileClientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId: fileClientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
manager.prepareUpload(url, {clientId: fileClientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId: fileClientId, localPath: 'path1'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(fileClientId)).toBe(true);
|
||||
|
||||
expect(uploadMocks.resolvePromise).not.toBeNull();
|
||||
uploadMocks.resolvePromise!({ok: true, code: 500, data: {file_infos: [{clientId: fileClientId, id: fileServerId}]}});
|
||||
uploadMocks.resolvePromise!({ok: true, code: 500, data: {file_infos: [{clientId: fileClientId, id: fileServerId, localPath: 'path1'}]}});
|
||||
|
||||
// Wait for other promises (on complete write) to finish
|
||||
await new Promise(process.nextTick);
|
||||
|
|
@ -284,9 +284,9 @@ describe('draft upload manager', () => {
|
|||
const uploadMocks = mockUpload();
|
||||
|
||||
const clientId = 'clientId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
manager.prepareUpload(url, {clientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId, localPath: 'path1'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(clientId)).toBe(true);
|
||||
|
||||
expect(uploadMocks.resolvePromise).not.toBeNull();
|
||||
|
|
@ -308,9 +308,9 @@ describe('draft upload manager', () => {
|
|||
const uploadMocks = mockUpload();
|
||||
|
||||
const clientId = 'clientId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
manager.prepareUpload(url, {clientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId, localPath: 'path1'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(clientId)).toBe(true);
|
||||
|
||||
expect(uploadMocks.resolvePromise).not.toBeNull();
|
||||
|
|
@ -332,13 +332,13 @@ describe('draft upload manager', () => {
|
|||
const uploadMocks = mockUpload();
|
||||
|
||||
const clientId = 'clientId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
const nullProgressHandler = jest.fn();
|
||||
let cancelProgressHandler = manager.registerProgressHandler(clientId, nullProgressHandler);
|
||||
expect(cancelProgressHandler).toBeNull();
|
||||
|
||||
manager.prepareUpload(url, {clientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId, localPath: 'path1'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(clientId)).toBe(true);
|
||||
|
||||
const progressHandler = jest.fn();
|
||||
|
|
@ -373,13 +373,13 @@ describe('draft upload manager', () => {
|
|||
const uploadMocks = mockUpload();
|
||||
|
||||
const clientId = 'clientId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
const nullErrorHandler = jest.fn();
|
||||
let cancelErrorHandler = manager.registerProgressHandler(clientId, nullErrorHandler);
|
||||
expect(cancelErrorHandler).toBeNull();
|
||||
|
||||
manager.prepareUpload(url, {clientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId, localPath: 'path1'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(clientId)).toBe(true);
|
||||
|
||||
const errorHandler = jest.fn();
|
||||
|
|
@ -409,13 +409,13 @@ describe('draft upload manager', () => {
|
|||
const uploadMocks = mockUpload();
|
||||
|
||||
const clientId = 'clientId';
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId} as FileInfo]);
|
||||
await addFilesToDraft(url, channelId, rootId, [{clientId, localPath: 'path1'} as FileInfo]);
|
||||
|
||||
const nullErrorHandler = jest.fn();
|
||||
let cancelErrorHandler = manager.registerProgressHandler(clientId, nullErrorHandler);
|
||||
expect(cancelErrorHandler).toBeNull();
|
||||
|
||||
manager.prepareUpload(url, {clientId} as FileInfo, channelId, rootId, 0);
|
||||
manager.prepareUpload(url, {clientId, localPath: 'path1'} as FileInfo, channelId, rootId, 0);
|
||||
expect(manager.isUploading(clientId)).toBe(true);
|
||||
|
||||
const errorHandler = jest.fn();
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ export async function deleteChannelMembership(operator: ServerDataOperator, user
|
|||
models.push(membership.prepareDestroyPermanently());
|
||||
}
|
||||
if (models.length && !prepareRecordsOnly) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'deleteChannelMembership');
|
||||
}
|
||||
return {models};
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export async function deletePreferences(database: ServerDatabase, preferences: P
|
|||
}
|
||||
}
|
||||
if (preparedModels.length) {
|
||||
await database.operator.batchRecords(preparedModels);
|
||||
await database.operator.batchRecords(preparedModels, 'deletePreferences');
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ export async function setCurrentChannelId(operator: ServerDataOperator, channelI
|
|||
try {
|
||||
const models = await prepareCommonSystemValues(operator, {currentChannelId: channelId});
|
||||
if (models) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'setCurrentChannelId');
|
||||
}
|
||||
|
||||
return {currentChannelId: channelId};
|
||||
|
|
@ -436,7 +436,7 @@ export async function setCurrentTeamAndChannelId(operator: ServerDataOperator, t
|
|||
currentTeamId: teamId,
|
||||
});
|
||||
if (models) {
|
||||
await operator.batchRecords(models);
|
||||
await operator.batchRecords(models, 'setCurrentTeamAndChannelId');
|
||||
}
|
||||
|
||||
return {currentTeamId: teamId, currentChannelId: channelId};
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ export async function deleteMyTeams(operator: ServerDataOperator, myTeams: MyTea
|
|||
}
|
||||
|
||||
if (preparedModels.length) {
|
||||
await operator.batchRecords(preparedModels);
|
||||
await operator.batchRecords(preparedModels, 'deleteMyTeams');
|
||||
}
|
||||
return {};
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class MixinBuilder {
|
||||
superclass: any;
|
||||
constructor(superclass: any) {
|
||||
class MixinBuilder<T> {
|
||||
superclass: T;
|
||||
constructor(superclass: T) {
|
||||
this.superclass = superclass;
|
||||
}
|
||||
|
||||
with(...mixins: any[]) {
|
||||
with(...mixins: any[]): T {
|
||||
return mixins.reduce((c, mixin) => mixin(c), this.superclass);
|
||||
}
|
||||
}
|
||||
|
||||
const mix = (superclass: any) => new MixinBuilder(superclass);
|
||||
const mix = <T>(superclass: T) => new MixinBuilder(superclass);
|
||||
|
||||
export default mix;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
diff --git a/node_modules/@nozbe/watermelondb/Database/index.js b/node_modules/@nozbe/watermelondb/Database/index.js
|
||||
index 8d71c6f..9ad75d6 100644
|
||||
index 8d71c6f..30832c8 100644
|
||||
--- a/node_modules/@nozbe/watermelondb/Database/index.js
|
||||
+++ b/node_modules/@nozbe/watermelondb/Database/index.js
|
||||
@@ -126,6 +126,10 @@ var Database = /*#__PURE__*/function () {
|
||||
@@ -91,7 +91,9 @@ var Database = /*#__PURE__*/function () {
|
||||
|
||||
if (!preparedState) {
|
||||
(0, _common.invariant)('disposable' !== record._raw._status, "Cannot batch a disposable record");
|
||||
- throw new Error("Cannot batch a record that doesn't have a prepared create/update/delete");
|
||||
+ //throw new Error("Cannot batch a record that doesn't have a prepared create/update/delete");
|
||||
+ console.debug('Trying to batch a record with no prepared state on table', record.constructor.table)
|
||||
+ return;
|
||||
}
|
||||
|
||||
var raw = record._raw;
|
||||
@@ -126,6 +128,10 @@ var Database = /*#__PURE__*/function () {
|
||||
// subsequent changes to the record don't trip up the invariant
|
||||
// TODO: What if this fails?
|
||||
record._preparedState = null;
|
||||
|
|
@ -27,27 +38,37 @@ index 96114ec..ecfe3c1 100644
|
|||
|
||||
prepareDestroyPermanently(): this
|
||||
diff --git a/node_modules/@nozbe/watermelondb/Model/index.js b/node_modules/@nozbe/watermelondb/Model/index.js
|
||||
index b0e3a83..57ecb8d 100644
|
||||
index b0e3a83..1bbce74 100644
|
||||
--- a/node_modules/@nozbe/watermelondb/Model/index.js
|
||||
+++ b/node_modules/@nozbe/watermelondb/Model/index.js
|
||||
@@ -81,7 +81,7 @@ var Model = /*#__PURE__*/function () {
|
||||
@@ -81,7 +81,17 @@ var Model = /*#__PURE__*/function () {
|
||||
_proto.prepareUpdate = function prepareUpdate(recordUpdater = _noop.default) {
|
||||
var _this = this;
|
||||
|
||||
- (0, _invariant.default)(!this._preparedState, "Cannot update a record with pending changes");
|
||||
+ (0, _invariant.default)(!this._preparedState, "Cannot update a record with pending changes in table " + _this.table);
|
||||
+ if ('deleted' === this._raw._status) {
|
||||
+ console.debug("Updating a deleted record in table " + _this.table)
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
+ //(0, _invariant.default)(!this._preparedState, "Cannot update a record with pending changes");
|
||||
+ if (this._preparedState) {
|
||||
+ console.debug("Updating a record with pending changes in table " + _this.table)
|
||||
+ } else {
|
||||
+ this.__original = Object.assign({}, this._raw);
|
||||
+ }
|
||||
|
||||
this.__ensureNotDisposable("Model.prepareUpdate()");
|
||||
|
||||
@@ -92,6 +92,7 @@ var Model = /*#__PURE__*/function () {
|
||||
@@ -91,7 +101,6 @@ var Model = /*#__PURE__*/function () {
|
||||
this._setRaw((0, _Schema.columnName)('updated_at'), Date.now());
|
||||
} // Perform updates
|
||||
|
||||
|
||||
+ this.__original = Object.assign({}, this._raw);
|
||||
-
|
||||
(0, _ensureSync.default)(recordUpdater(this));
|
||||
this._isEditing = false;
|
||||
this._preparedState = 'update'; // TODO: `process.nextTick` doesn't work on React Native
|
||||
@@ -107,6 +108,19 @@ var Model = /*#__PURE__*/function () {
|
||||
@@ -107,6 +116,19 @@ var Model = /*#__PURE__*/function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
|
|
@ -67,6 +88,18 @@ index b0e3a83..57ecb8d 100644
|
|||
_proto.prepareMarkAsDeleted = function prepareMarkAsDeleted() {
|
||||
(0, _invariant.default)(!this._preparedState, "Cannot mark a record with pending changes as deleted");
|
||||
|
||||
@@ -118,7 +140,10 @@ var Model = /*#__PURE__*/function () {
|
||||
};
|
||||
|
||||
_proto.prepareDestroyPermanently = function prepareDestroyPermanently() {
|
||||
- (0, _invariant.default)(!this._preparedState, "Cannot destroy permanently a record with pending changes");
|
||||
+ //(0, _invariant.default)(!this._preparedState, "Cannot destroy permanently a record with pending changes");
|
||||
+ if (this._preparedState) {
|
||||
+ console.debug("Deleting a record with pending changes in table " + this.table);
|
||||
+ }
|
||||
|
||||
this.__ensureNotDisposable("Model.prepareDestroyPermanently()");
|
||||
|
||||
diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt
|
||||
index ca31e20..b45c753 100644
|
||||
--- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class TestHelper {
|
|||
currentUserId: this.basicUser!.id,
|
||||
});
|
||||
if (systems?.length) {
|
||||
await operator.batchRecords(systems);
|
||||
await operator.batchRecords(systems, 'test');
|
||||
}
|
||||
|
||||
await operator.handleSystem({
|
||||
|
|
@ -459,23 +459,23 @@ class TestHelper {
|
|||
};
|
||||
|
||||
mockLogin = () => {
|
||||
nock(this.basicClient?.getBaseRoute()).
|
||||
nock(this.basicClient?.getBaseRoute() || '').
|
||||
post('/users/login').
|
||||
reply(200, this.basicUser!, {'X-Version-Id': 'Server Version'});
|
||||
|
||||
nock(this.basicClient?.getBaseRoute()).
|
||||
nock(this.basicClient?.getBaseRoute() || '').
|
||||
get('/users/me/teams/members').
|
||||
reply(200, [this.basicTeamMember]);
|
||||
|
||||
nock(this.basicClient?.getBaseRoute()).
|
||||
nock(this.basicClient?.getBaseRoute() || '').
|
||||
get('/users/me/teams/unread').
|
||||
reply(200, [{team_id: this.basicTeam!.id, msg_count: 0, mention_count: 0}]);
|
||||
|
||||
nock(this.basicClient?.getBaseRoute()).
|
||||
nock(this.basicClient?.getBaseRoute() || '').
|
||||
get('/users/me/teams').
|
||||
reply(200, [this.basicTeam]);
|
||||
|
||||
nock(this.basicClient?.getBaseRoute()).
|
||||
nock(this.basicClient?.getBaseRoute() || '').
|
||||
get('/users/me/preferences').
|
||||
reply(200, [{user_id: this.basicUser!.id, category: 'tutorial_step', name: this.basicUser!.id, value: '999'}]);
|
||||
};
|
||||
|
|
@ -563,7 +563,7 @@ class TestHelper {
|
|||
};
|
||||
|
||||
initBasic = async (client = this.createClient()) => {
|
||||
client.setUrl(Config.TestServerUrl || Config.DefaultServerUrl);
|
||||
client.apiClient.baseUrl = Config.TestServerUrl || Config.DefaultServerUrl;
|
||||
this.basicClient = client;
|
||||
|
||||
this.initMockEntities();
|
||||
|
|
|
|||
|
|
@ -55,12 +55,12 @@ export type PrepareBaseRecordArgs = TransformerArgs & {
|
|||
fieldsMapper: (model: Model) => void;
|
||||
}
|
||||
|
||||
export type OperationArgs = {
|
||||
export type OperationArgs<T extends Model> = {
|
||||
tableName: string;
|
||||
createRaws?: RecordPair[];
|
||||
updateRaws?: RecordPair[];
|
||||
deleteRaws?: Model[];
|
||||
transformer: (args: TransformerArgs) => Promise<Model>;
|
||||
deleteRaws?: T[];
|
||||
transformer: (args: TransformerArgs) => Promise<T>;
|
||||
};
|
||||
|
||||
export type Models = Array<Class<Model>>;
|
||||
|
|
@ -157,10 +157,10 @@ export type ProcessRecordsArgs = {
|
|||
buildKeyRecordBy?: (obj: Record<string, any>) => string;
|
||||
};
|
||||
|
||||
export type HandleRecordsArgs = {
|
||||
export type HandleRecordsArgs<T extends Model> = {
|
||||
buildKeyRecordBy?: (obj: Record<string, any>) => string;
|
||||
fieldName: string;
|
||||
transformer: (args: TransformerArgs) => Promise<Model>;
|
||||
transformer: (args: TransformerArgs) => Promise<T>;
|
||||
createOrUpdateRawValues: RawValue[];
|
||||
deleteRawValues?: RawValue[];
|
||||
tableName: string;
|
||||
|
|
@ -311,8 +311,8 @@ export type GetDatabaseConnectionArgs = {
|
|||
setAsActiveDatabase: boolean;
|
||||
}
|
||||
|
||||
export type ProcessRecordResults = {
|
||||
export type ProcessRecordResults<T extends Model> = {
|
||||
createRaws: RecordPair[];
|
||||
updateRaws: RecordPair[];
|
||||
deleteRaws: Model[];
|
||||
deleteRaws: T[];
|
||||
}
|
||||
|
|
|
|||
3
types/utils/mixins.d.ts
vendored
Normal file
3
types/utils/mixins.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
type Constructor<T={}> = new (...args: any[]) => T;
|
||||
Loading…
Reference in a new issue