MM-33224 [v2] Data Operator Team section (#5272)

* MM_33224 : Team [IN PROGRESS]

* MM_33224 : Updating test for Team schema after addition of update_at column

* MM_33224 : Team Entity - Completed

* MM_33224 - TeamChannelHistory - Completed

* MM_33224 : Removing duplicates RawValues before processing them

* MM-33224 : TeamSearchHistory - Completed

* MM-33224 : Slash Command - Completed

* MM-33224 : My Team - Completed

* MM-33227 [v2] Data Operator Channel section (#5277)

* MM_33227 : Channel[IN PROGRESS]

* MM_33227 : Channel - Completed

* MM-33227 : MyChannelSettings - Completed

* MM-33227 : ChannelInfo - Completed

* MM-33227 :  MyChannel - Completed

* MM-33227 : Added expected results in handlers' test

* MM_33227 : Renamed RawApp and RawServers fields

* MM_33227 : Cleaning up Role

* MM_33227 : Cleaning TOS

* MM-33227 : Cleaning up Group comparator

* MM-33227 : Updated JSDoc

* MM-33227 : Fixed 'comparators' to comparator in JSDoc

Co-authored-by: Avinash Lingaloo <>

Co-authored-by: Avinash Lingaloo <>
This commit is contained in:
Avinash Lingaloo
2021-04-09 10:08:32 +04:00
committed by GitHub
parent 3279cf808b
commit 661904fbaf
22 changed files with 2013 additions and 253 deletions

View File

@@ -1,26 +1,36 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {App} from '@database/default/models';
import {Role, User} from '@database/server/models';
import App from '@typings/database/app';
import Channel from '@typings/database/channel';
import ChannelInfo from '@typings/database/channel_info';
import ChannelMembership from '@typings/database/channel_membership';
import CustomEmoji from '@typings/database/custom_emoji';
import {
RawApp,
RawChannel,
RawChannelInfo,
RawChannelMembership,
RawCustomEmoji,
RawDraft,
RawGlobal,
RawGroup,
RawGroupMembership,
RawGroupsInTeam,
RawGroupsInChannel,
RawGroupsInTeam,
RawMyChannel,
RawMyChannelSettings,
RawMyTeam,
RawPost,
RawPreference,
RawRole,
RawServers,
RawSlashCommand,
RawSystem,
RawTeam,
RawTeamChannelHistory,
RawTeamMembership,
RawTeamSearchHistory,
RawTermsOfService,
RawUser,
} from '@typings/database/database';
@@ -30,12 +40,21 @@ import Group from '@typings/database/group';
import GroupMembership from '@typings/database/group_membership';
import GroupsInChannel from '@typings/database/groups_in_channel';
import GroupsInTeam from '@typings/database/groups_in_team';
import MyChannel from '@typings/database/my_channel';
import MyChannelSettings from '@typings/database/my_channel_settings';
import MyTeam from '@typings/database/my_team';
import Post from '@typings/database/post';
import Preference from '@typings/database/preference';
import Role from '@typings/database/role';
import Servers from '@typings/database/servers';
import SlashCommand from '@typings/database/slash_command';
import System from '@typings/database/system';
import Team from '@typings/database/team';
import TeamChannelHistory from '@typings/database/team_channel_history';
import TeamMembership from '@typings/database/team_membership';
import TeamSearchHistory from '@typings/database/team_search_history';
import TermsOfService from '@typings/database/terms_of_service';
import User from '@typings/database/user';
/**
* This file contains all the comparators that are used by the handlers to find out which records to truly update and
@@ -46,9 +65,9 @@ import TermsOfService from '@typings/database/terms_of_service';
export const isRecordAppEqualToRaw = (record: App, raw: RawApp) => {
return (
raw.buildNumber === record.buildNumber &&
raw.createdAt === record.createdAt &&
raw.versionNumber === record.versionNumber
raw.build_number === record.buildNumber &&
raw.created_at === record.createdAt &&
raw.version_number === record.versionNumber
);
};
@@ -57,19 +76,19 @@ export const isRecordGlobalEqualToRaw = (record: Global, raw: RawGlobal) => {
};
export const isRecordServerEqualToRaw = (record: Servers, raw: RawServers) => {
return raw.url === record.url && raw.dbPath === record.dbPath;
return raw.url === record.url && raw.db_path === record.dbPath;
};
export const isRecordRoleEqualToRaw = (record: Role, raw: RawRole) => {
return raw.name === record.name && JSON.stringify(raw.permissions) === JSON.stringify(record.permissions);
return raw.id === record.id;
};
export const isRecordSystemEqualToRaw = (record: System, raw: RawSystem) => {
return raw.name === record.name && raw.value === record.value;
return raw.id === record.id;
};
export const isRecordTermsOfServiceEqualToRaw = (record: TermsOfService, raw: RawTermsOfService) => {
return raw.acceptedAt === record.acceptedAt;
return raw.id === record.id;
};
export const isRecordDraftEqualToRaw = (record: Draft, raw: RawDraft) => {
@@ -88,8 +107,7 @@ export const isRecordPreferenceEqualToRaw = (record: Preference, raw: RawPrefere
return (
raw.category === record.category &&
raw.name === record.name &&
raw.user_id === record.userId &&
raw.value === record.value
raw.user_id === record.userId
);
};
@@ -98,7 +116,7 @@ export const isRecordTeamMembershipEqualToRaw = (record: TeamMembership, raw: Ra
};
export const isRecordCustomEmojiEqualToRaw = (record: CustomEmoji, raw: RawCustomEmoji) => {
return raw.name === record.name;
return raw.id === record.id;
};
export const isRecordGroupMembershipEqualToRaw = (record: GroupMembership, raw: RawGroupMembership) => {
@@ -110,7 +128,7 @@ export const isRecordChannelMembershipEqualToRaw = (record: ChannelMembership, r
};
export const isRecordGroupEqualToRaw = (record: Group, raw: RawGroup) => {
return raw.name === record.name && raw.display_name === record.displayName;
return raw.id === record.id;
};
export const isRecordGroupsInTeamEqualToRaw = (record: GroupsInTeam, raw: RawGroupsInTeam) => {
@@ -120,3 +138,39 @@ export const isRecordGroupsInTeamEqualToRaw = (record: GroupsInTeam, raw: RawGro
export const isRecordGroupsInChannelEqualToRaw = (record: GroupsInChannel, raw: RawGroupsInChannel) => {
return raw.channel_id === record.channelId && raw.group_id === record.groupId;
};
export const isRecordTeamEqualToRaw = (record: Team, raw: RawTeam) => {
return raw.id === record.id;
};
export const isRecordTeamChannelHistoryEqualToRaw = (record: TeamChannelHistory, raw: RawTeamChannelHistory) => {
return raw.team_id === record.teamId;
};
export const isRecordTeamSearchHistoryEqualToRaw = (record: TeamSearchHistory, raw: RawTeamSearchHistory) => {
return raw.team_id === record.teamId && raw.term === record.term;
};
export const isRecordSlashCommandEqualToRaw = (record: SlashCommand, raw: RawSlashCommand) => {
return raw.id === record.id;
};
export const isRecordMyTeamEqualToRaw = (record: MyTeam, raw: RawMyTeam) => {
return raw.team_id === record.teamId;
};
export const isRecordChannelEqualToRaw = (record: Channel, raw: RawChannel) => {
return raw.id === record.id;
};
export const isRecordMyChannelSettingsEqualToRaw = (record: MyChannelSettings, raw: RawMyChannelSettings) => {
return raw.channel_id === record.channelId;
};
export const isRecordChannelInfoEqualToRaw = (record: ChannelInfo, raw: RawChannelInfo) => {
return raw.channel_id === record.channelId;
};
export const isRecordMyChannelEqualToRaw = (record: MyChannel, raw: RawMyChannel) => {
return raw.channel_id === record.channelId;
};

View File

@@ -7,6 +7,8 @@ import Model from '@nozbe/watermelondb/Model';
import {MM_TABLES} from '@constants/database';
import {
isRecordAppEqualToRaw,
isRecordChannelEqualToRaw,
isRecordChannelInfoEqualToRaw,
isRecordChannelMembershipEqualToRaw,
isRecordCustomEmojiEqualToRaw,
isRecordDraftEqualToRaw,
@@ -15,12 +17,19 @@ import {
isRecordGroupMembershipEqualToRaw,
isRecordGroupsInChannelEqualToRaw,
isRecordGroupsInTeamEqualToRaw,
isRecordMyChannelEqualToRaw,
isRecordMyChannelSettingsEqualToRaw,
isRecordMyTeamEqualToRaw,
isRecordPostEqualToRaw,
isRecordPreferenceEqualToRaw,
isRecordRoleEqualToRaw,
isRecordServerEqualToRaw,
isRecordSlashCommandEqualToRaw,
isRecordSystemEqualToRaw,
isRecordTeamChannelHistoryEqualToRaw,
isRecordTeamEqualToRaw,
isRecordTeamMembershipEqualToRaw,
isRecordTeamSearchHistoryEqualToRaw,
isRecordTermsOfServiceEqualToRaw,
isRecordUserEqualToRaw,
} from '@database/admin/data_operator/comparators';
@@ -40,6 +49,8 @@ import {
PrepareForDatabaseArgs,
PrepareRecordsArgs,
ProcessInputsArgs,
RawChannel,
RawChannelInfo,
RawChannelMembership,
RawCustomEmoji,
RawDraft,
@@ -49,12 +60,19 @@ import {
RawGroupMembership,
RawGroupsInChannel,
RawGroupsInTeam,
RawMyChannel,
RawMyChannelSettings,
RawMyTeam,
RawPost,
RawPostMetadata,
RawPostsInThread,
RawPreference,
RawReaction,
RawSlashCommand,
RawTeam,
RawTeamChannelHistory,
RawTeamMembership,
RawTeamSearchHistory,
RawUser,
RawValue,
} from '@typings/database/database';
@@ -70,7 +88,9 @@ import DataOperatorException from '../../exceptions/data_operator_exception';
import DatabaseConnectionException from '../../exceptions/database_connection_exception';
import {
operateAppRecord,
operateChannelInfoRecord,
operateChannelMembershipRecord,
operateChannelRecord,
operateCustomEmojiRecord,
operateDraftRecord,
operateFileRecord,
@@ -79,6 +99,9 @@ import {
operateGroupRecord,
operateGroupsInChannelRecord,
operateGroupsInTeamRecord,
operateMyChannelRecord,
operateMyChannelSettingsRecord,
operateMyTeamRecord,
operatePostInThreadRecord,
operatePostMetadataRecord,
operatePostRecord,
@@ -87,8 +110,12 @@ import {
operateReactionRecord,
operateRoleRecord,
operateServersRecord,
operateSlashCommandRecord,
operateSystemRecord,
operateTeamChannelHistoryRecord,
operateTeamMembershipRecord,
operateTeamRecord,
operateTeamSearchHistoryRecord,
operateTermsOfServiceRecord,
operateUserRecord,
} from '../operators';
@@ -96,6 +123,7 @@ import {
createPostsChain,
getRangeOfValues,
getRawRecordPairs,
getUniqueRawsBy,
hasSimilarUpdateAt,
retrieveRecords,
sanitizePosts,
@@ -103,6 +131,8 @@ import {
} from '../utils';
const {
CHANNEL,
CHANNEL_INFO,
CHANNEL_MEMBERSHIP,
CUSTOM_EMOJI,
DRAFT,
@@ -111,18 +141,23 @@ const {
GROUPS_IN_CHANNEL,
GROUPS_IN_TEAM,
GROUP_MEMBERSHIP,
MY_CHANNEL,
MY_CHANNEL_SETTINGS,
MY_TEAM,
POST,
POSTS_IN_CHANNEL,
POSTS_IN_THREAD,
POST_METADATA,
PREFERENCE,
REACTION,
SLASH_COMMAND,
TEAM,
TEAM_CHANNEL_HISTORY,
TEAM_MEMBERSHIP,
TEAM_SEARCH_HISTORY,
USER,
} = MM_TABLES.SERVER;
// TODO : We assume that we are receiving clean&correct data from the server. Hence, we can never have two posts in an array with the same post_id. This should be confirmed.
class DataOperator {
/**
* serverDatabase : In a multi-server configuration, this connection will be used by WebSockets and other parties to update databases other than the active one.
@@ -143,9 +178,10 @@ class DataOperator {
* @returns {Promise<void>}
*/
handleIsolatedEntity = async ({tableName, values}: HandleIsolatedEntityArgs) => {
let comparator;
let findMatchingRecordBy;
let fieldName;
let operator;
let rawValues;
if (!values.length) {
throw new DataOperatorException(
@@ -155,45 +191,52 @@ class DataOperator {
switch (tableName) {
case IsolatedEntities.APP: {
comparator = isRecordAppEqualToRaw;
findMatchingRecordBy = isRecordAppEqualToRaw;
fieldName = 'version_number';
operator = operateAppRecord;
rawValues = getUniqueRawsBy({raws: values, key: 'versionNumber'});
break;
}
case IsolatedEntities.CUSTOM_EMOJI: {
comparator = isRecordCustomEmojiEqualToRaw;
fieldName = 'name';
findMatchingRecordBy = isRecordCustomEmojiEqualToRaw;
fieldName = 'id';
operator = operateCustomEmojiRecord;
rawValues = getUniqueRawsBy({raws: values, key: 'id'});
break;
}
case IsolatedEntities.GLOBAL: {
comparator = isRecordGlobalEqualToRaw;
findMatchingRecordBy = isRecordGlobalEqualToRaw;
fieldName = 'name';
operator = operateGlobalRecord;
rawValues = getUniqueRawsBy({raws: values, key: 'name'});
break;
}
case IsolatedEntities.ROLE: {
comparator = isRecordRoleEqualToRaw;
fieldName = 'name';
findMatchingRecordBy = isRecordRoleEqualToRaw;
fieldName = 'id';
operator = operateRoleRecord;
rawValues = getUniqueRawsBy({raws: values, key: 'id'});
break;
}
case IsolatedEntities.SERVERS: {
comparator = isRecordServerEqualToRaw;
fieldName = 'db_path';
findMatchingRecordBy = isRecordServerEqualToRaw;
fieldName = 'url';
operator = operateServersRecord;
rawValues = getUniqueRawsBy({raws: values, key: 'displayName'});
break;
}
case IsolatedEntities.SYSTEM: {
comparator = isRecordSystemEqualToRaw;
fieldName = 'name';
findMatchingRecordBy = isRecordSystemEqualToRaw;
fieldName = 'id';
operator = operateSystemRecord;
rawValues = getUniqueRawsBy({raws: values, key: 'id'});
break;
}
case IsolatedEntities.TERMS_OF_SERVICE: {
comparator = isRecordTermsOfServiceEqualToRaw;
fieldName = 'accepted_at';
findMatchingRecordBy = isRecordTermsOfServiceEqualToRaw;
fieldName = 'id';
operator = operateTermsOfServiceRecord;
rawValues = getUniqueRawsBy({raws: values, key: 'id'});
break;
}
default: {
@@ -203,12 +246,12 @@ class DataOperator {
}
}
if (operator && fieldName && comparator) {
if (operator && fieldName && findMatchingRecordBy) {
await this.handleEntityRecords({
comparator,
findMatchingRecordBy,
fieldName,
operator,
rawValues: values,
rawValues,
tableName,
});
}
@@ -226,11 +269,13 @@ class DataOperator {
);
}
const rawValues = getUniqueRawsBy({raws: drafts, key: 'channel_id'});
await this.handleEntityRecords({
comparator: isRecordDraftEqualToRaw,
findMatchingRecordBy: isRecordDraftEqualToRaw,
fieldName: 'channel_id',
operator: operateDraftRecord,
rawValues: drafts,
rawValues,
tableName: DRAFT,
});
};
@@ -250,6 +295,8 @@ class DataOperator {
);
}
const rawValues = getUniqueRawsBy({raws: reactions, key: 'emoji_name'}) as RawReaction[];
const database = await this.getDatabase(REACTION);
const {
@@ -259,7 +306,7 @@ class DataOperator {
} = await sanitizeReactions({
database,
post_id: reactions[0].post_id,
rawReactions: reactions,
rawReactions: rawValues,
});
let batchRecords: Model[] = [];
@@ -320,9 +367,11 @@ class DataOperator {
);
}
const rawValues = getUniqueRawsBy({raws: values, key: 'id'}) as RawPost[];
// By sanitizing the values, we are separating 'posts' that needs updating ( i.e. un-ordered posts ) from those that need to be created in our database
const {postsOrdered, postsUnordered} = sanitizePosts({
posts: values,
posts: rawValues,
orders,
});
@@ -330,7 +379,7 @@ class DataOperator {
const futureEntries = await this.processInputs({
rawValues: postsOrdered,
tableName,
comparator: isRecordPostEqualToRaw,
findMatchingRecordBy: isRecordPostEqualToRaw,
fieldName: 'id',
});
@@ -450,7 +499,7 @@ class DataOperator {
if (postsUnordered.length) {
// Truly update those posts that have a different update_at value
await this.handleEntityRecords({
comparator: isRecordPostEqualToRaw,
findMatchingRecordBy: isRecordPostEqualToRaw,
fieldName: 'id',
operator: operatePostRecord,
rawValues: postsUnordered,
@@ -720,11 +769,14 @@ class DataOperator {
'An empty "users" array has been passed to the handleUsers method',
);
}
const rawValues = getUniqueRawsBy({raws: users, key: 'id'});
await this.handleEntityRecords({
comparator: isRecordUserEqualToRaw,
findMatchingRecordBy: isRecordUserEqualToRaw,
fieldName: 'id',
operator: operateUserRecord,
rawValues: users,
rawValues,
tableName: USER,
});
};
@@ -742,11 +794,13 @@ class DataOperator {
);
}
const rawValues = getUniqueRawsBy({raws: preferences, key: 'name'});
await this.handleEntityRecords({
comparator: isRecordPreferenceEqualToRaw,
findMatchingRecordBy: isRecordPreferenceEqualToRaw,
fieldName: 'user_id',
operator: operatePreferenceRecord,
rawValues: preferences,
rawValues,
tableName: PREFERENCE,
});
};
@@ -763,11 +817,14 @@ class DataOperator {
'An empty "teamMemberships" array has been passed to the handleTeamMemberships method',
);
}
const rawValues = getUniqueRawsBy({raws: teamMemberships, key: 'team_id'});
await this.handleEntityRecords({
comparator: isRecordTeamMembershipEqualToRaw,
findMatchingRecordBy: isRecordTeamMembershipEqualToRaw,
fieldName: 'user_id',
operator: operateTeamMembershipRecord,
rawValues: teamMemberships,
rawValues,
tableName: TEAM_MEMBERSHIP,
});
};
@@ -785,11 +842,13 @@ class DataOperator {
);
}
const rawValues = getUniqueRawsBy({raws: groupMemberships, key: 'group_id'});
await this.handleEntityRecords({
comparator: isRecordGroupMembershipEqualToRaw,
findMatchingRecordBy: isRecordGroupMembershipEqualToRaw,
fieldName: 'user_id',
operator: operateGroupMembershipRecord,
rawValues: groupMemberships,
rawValues,
tableName: GROUP_MEMBERSHIP,
});
};
@@ -807,11 +866,13 @@ class DataOperator {
);
}
const rawValues = getUniqueRawsBy({raws: channelMemberships, key: 'channel_id'});
await this.handleEntityRecords({
comparator: isRecordChannelMembershipEqualToRaw,
findMatchingRecordBy: isRecordChannelMembershipEqualToRaw,
fieldName: 'user_id',
operator: operateChannelMembershipRecord,
rawValues: channelMemberships,
rawValues,
tableName: CHANNEL_MEMBERSHIP,
});
};
@@ -829,70 +890,292 @@ class DataOperator {
);
}
const rawValues = getUniqueRawsBy({raws: groups, key: 'name'});
await this.handleEntityRecords({
comparator: isRecordGroupEqualToRaw,
findMatchingRecordBy: isRecordGroupEqualToRaw,
fieldName: 'name',
operator: operateGroupRecord,
rawValues: groups,
rawValues,
tableName: GROUP,
});
};
/**
* handleGroupsInTeam: Handler responsible for the Create/Update operations occurring on the GROUPS_IN_TEAM entity from the 'Server' schema
* @param {RawGroupsInTeam[]} groupsInTeam
* @param {RawGroupsInTeam[]} groupsInTeams
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleGroupsInTeam = async (groupsInTeam: RawGroupsInTeam[]) => {
if (!groupsInTeam.length) {
handleGroupsInTeam = async (groupsInTeams: RawGroupsInTeam[]) => {
if (!groupsInTeams.length) {
throw new DataOperatorException(
'An empty "groups" array has been passed to the handleGroupsInTeam method',
);
}
const rawValues = getUniqueRawsBy({raws: groupsInTeams, key: 'group_id'});
await this.handleEntityRecords({
comparator: isRecordGroupsInTeamEqualToRaw,
findMatchingRecordBy: isRecordGroupsInTeamEqualToRaw,
fieldName: 'group_id',
operator: operateGroupsInTeamRecord,
rawValues: groupsInTeam,
rawValues,
tableName: GROUPS_IN_TEAM,
});
};
/**
* handleGroupsInChannel: Handler responsible for the Create/Update operations occurring on the GROUPS_IN_CHANNEL entity from the 'Server' schema
* @param {RawGroupsInChannel[]} groupsInChannel
* @param {RawGroupsInChannel[]} groupsInChannels
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleGroupsInChannel = async (groupsInChannel: RawGroupsInChannel[]) => {
if (!groupsInChannel.length) {
handleGroupsInChannel = async (groupsInChannels: RawGroupsInChannel[]) => {
if (!groupsInChannels.length) {
throw new DataOperatorException(
'An empty "groups" array has been passed to the handleGroupsInTeam method',
);
}
const rawValues = getUniqueRawsBy({raws: groupsInChannels, key: 'channel_id'});
await this.handleEntityRecords({
comparator: isRecordGroupsInChannelEqualToRaw,
findMatchingRecordBy: isRecordGroupsInChannelEqualToRaw,
fieldName: 'group_id',
operator: operateGroupsInChannelRecord,
rawValues: groupsInChannel,
rawValues,
tableName: GROUPS_IN_CHANNEL,
});
};
/**
* handleTeam: Handler responsible for the Create/Update operations occurring on the TEAM entity from the 'Server' schema
* @param {RawTeam[]} teams
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleTeam = async (teams: RawTeam[]) => {
if (!teams.length) {
throw new DataOperatorException(
'An empty "teams" array has been passed to the handleTeam method',
);
}
const rawValues = getUniqueRawsBy({raws: teams, key: 'id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordTeamEqualToRaw,
fieldName: 'id',
operator: operateTeamRecord,
rawValues,
tableName: TEAM,
});
};
/**
* handleTeamChannelHistory: Handler responsible for the Create/Update operations occurring on the TEAM_CHANNEL_HISTORY entity from the 'Server' schema
* @param {RawTeamChannelHistory[]} teamChannelHistories
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleTeamChannelHistory = async (teamChannelHistories: RawTeamChannelHistory[]) => {
if (!teamChannelHistories.length) {
throw new DataOperatorException(
'An empty "teamChannelHistories" array has been passed to the handleTeamChannelHistory method',
);
}
const rawValues = getUniqueRawsBy({raws: teamChannelHistories, key: 'team_id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordTeamChannelHistoryEqualToRaw,
fieldName: 'team_id',
operator: operateTeamChannelHistoryRecord,
rawValues,
tableName: TEAM_CHANNEL_HISTORY,
});
};
/**
* handleTeamSearchHistory: Handler responsible for the Create/Update operations occurring on the TEAM_SEARCH_HISTORY entity from the 'Server' schema
* @param {RawTeamSearchHistory[]} teamSearchHistories
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleTeamSearchHistory = async (teamSearchHistories: RawTeamSearchHistory[]) => {
if (!teamSearchHistories.length) {
throw new DataOperatorException(
'An empty "teamSearchHistories" array has been passed to the handleTeamSearchHistory method',
);
}
const rawValues = getUniqueRawsBy({raws: teamSearchHistories, key: 'term'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordTeamSearchHistoryEqualToRaw,
fieldName: 'team_id',
operator: operateTeamSearchHistoryRecord,
rawValues,
tableName: TEAM_SEARCH_HISTORY,
});
};
/**
* handleSlashCommand: Handler responsible for the Create/Update operations occurring on the SLASH_COMMAND entity from the 'Server' schema
* @param {RawSlashCommand[]} slashCommands
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleSlashCommand = async (slashCommands: RawSlashCommand[]) => {
if (!slashCommands.length) {
throw new DataOperatorException(
'An empty "slashCommands" array has been passed to the handleSlashCommand method',
);
}
const rawValues = getUniqueRawsBy({raws: slashCommands, key: 'id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordSlashCommandEqualToRaw,
fieldName: 'id',
operator: operateSlashCommandRecord,
rawValues,
tableName: SLASH_COMMAND,
});
};
/**
* handleMyTeam: Handler responsible for the Create/Update operations occurring on the MY_TEAM entity from the 'Server' schema
* @param {RawMyTeam[]} myTeams
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleMyTeam = async (myTeams: RawMyTeam[]) => {
if (!myTeams.length) {
throw new DataOperatorException(
'An empty "myTeams" array has been passed to the handleSlashCommand method',
);
}
const rawValues = getUniqueRawsBy({raws: myTeams, key: 'team_id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordMyTeamEqualToRaw,
fieldName: 'team_id',
operator: operateMyTeamRecord,
rawValues,
tableName: MY_TEAM,
});
};
/**
* handleChannel: Handler responsible for the Create/Update operations occurring on the CHANNEL entity from the 'Server' schema
* @param {RawChannel[]} channels
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleChannel = async (channels: RawChannel[]) => {
if (!channels.length) {
throw new DataOperatorException(
'An empty "channels" array has been passed to the handleChannel method',
);
}
const rawValues = getUniqueRawsBy({raws: channels, key: 'id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordChannelEqualToRaw,
fieldName: 'id',
operator: operateChannelRecord,
rawValues,
tableName: CHANNEL,
});
};
/**
* handleMyChannelSettings: Handler responsible for the Create/Update operations occurring on the MY_CHANNEL_SETTINGS entity from the 'Server' schema
* @param {RawMyChannelSettings[]} settings
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleMyChannelSettings = async (settings: RawMyChannelSettings[]) => {
if (!settings.length) {
throw new DataOperatorException(
'An empty "settings" array has been passed to the handleMyChannelSettings method',
);
}
const rawValues = getUniqueRawsBy({raws: settings, key: 'channel_id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordMyChannelSettingsEqualToRaw,
fieldName: 'channel_id',
operator: operateMyChannelSettingsRecord,
rawValues,
tableName: MY_CHANNEL_SETTINGS,
});
};
/**
* handleChannelInfo: Handler responsible for the Create/Update operations occurring on the CHANNEL_INFO entity from the 'Server' schema
* @param {RawChannelInfo[]} channelInfos
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleChannelInfo = async (channelInfos: RawChannelInfo[]) => {
if (!channelInfos.length) {
throw new DataOperatorException(
'An empty "channelInfos" array has been passed to the handleMyChannelSettings method',
);
}
const rawValues = getUniqueRawsBy({raws: channelInfos, key: 'channel_id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordChannelInfoEqualToRaw,
fieldName: 'channel_id',
operator: operateChannelInfoRecord,
rawValues,
tableName: CHANNEL_INFO,
});
};
/**
* handleMyChannel: Handler responsible for the Create/Update operations occurring on the MY_CHANNEL entity from the 'Server' schema
* @param {RawMyChannel[]} myChannels
* @throws DataOperatorException
* @returns {Promise<null|void>}
*/
handleMyChannel = async (myChannels: RawMyChannel[]) => {
if (!myChannels.length) {
throw new DataOperatorException(
'An empty "myChannels" array has been passed to the handleMyChannel method',
);
}
const rawValues = getUniqueRawsBy({raws: myChannels, key: 'channel_id'});
await this.handleEntityRecords({
findMatchingRecordBy: isRecordMyChannelEqualToRaw,
fieldName: 'channel_id',
operator: operateMyChannelRecord,
rawValues,
tableName: MY_CHANNEL,
});
};
/**
* handleEntityRecords : Utility that processes some entities' data against values already present in the database so as to avoid duplicity.
* @param {HandleEntityRecordsArgs} handleEntityRecords
* @param {(existing: Model, newElement: RawValue) => boolean} handleEntityRecords.comparator
* @param {string} handleEntityRecords.fieldName
* @param {(DataFactoryArgs) => Promise<Model | null>} handleEntityRecords.operator
* @param {RawValue[]} handleEntityRecords.rawValues
* @param {string} handleEntityRecords.tableName
* @param {HandleEntityRecordsArgs} handleEntityArgs
* @param {(existing: Model, newElement: RawValue) => boolean} handleEntityArgs.findMatchingRecordBy
* @param {string} handleEntityArgs.fieldName
* @param {(DataFactoryArgs) => Promise<Model | null>} handleEntityArgs.operator
* @param {RawValue[]} handleEntityArgs.rawValues
* @param {string} handleEntityArgs.tableName
* @returns {Promise<null | void>}
*/
private handleEntityRecords = async ({comparator, fieldName, operator, rawValues, tableName}: HandleEntityRecordsArgs) => {
private handleEntityRecords = async ({findMatchingRecordBy, fieldName, operator, rawValues, tableName}: HandleEntityRecordsArgs) => {
if (!rawValues.length) {
return null;
}
@@ -900,7 +1183,7 @@ class DataOperator {
const {createRaws, updateRaws} = await this.processInputs({
rawValues,
tableName,
comparator,
findMatchingRecordBy,
fieldName,
});
@@ -914,17 +1197,16 @@ class DataOperator {
return records;
};
// TODO : Add jest to processInputs
/**
* processInputs: This method weeds out duplicates entries. It may happen that we do multiple inserts for
* the same value. Hence, prior to that we query the database and pick only those values that are 'new' from the 'Raw' array.
* @param {ProcessInputsArgs} prepareRecords
* @param {RawValue[]} prepareRecords.rawValues
* @param {string} prepareRecords.tableName
* @param {string} prepareRecords.fieldName
* @param {(existing: Model, newElement: RawValue) => boolean} prepareRecords.comparator
* @param {ProcessInputsArgs} inputsArg
* @param {RawValue[]} inputsArg.rawValues
* @param {string} inputsArg.tableName
* @param {string} inputsArg.fieldName
* @param {(existing: Model, newElement: RawValue) => boolean} inputsArg.findMatchingRecordBy
*/
private processInputs = async ({rawValues, tableName, comparator, fieldName}: ProcessInputsArgs) => {
private processInputs = async ({rawValues, tableName, findMatchingRecordBy, fieldName}: ProcessInputsArgs) => {
// We will query an entity 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});
@@ -942,7 +1224,7 @@ class DataOperator {
if (existingRecords.length > 0) {
rawValues.map((newElement: RawValue) => {
const findIndex = existingRecords.findIndex((existing) => {
return comparator(existing, newElement);
return findMatchingRecordBy(existing, newElement);
});
// We found a record in the database that matches this element; hence, we'll proceed for an UPDATE operation

File diff suppressed because it is too large Load Diff

View File

@@ -5,13 +5,16 @@ import {Q} from '@nozbe/watermelondb';
import Model from '@nozbe/watermelondb/Model';
import {MM_TABLES} from '@constants/database';
import {User} from '@database/server/models';
import App from '@typings/database/app';
import Channel from '@typings/database/channel';
import ChannelInfo from '@typings/database/channel_info';
import ChannelMembership from '@typings/database/channel_membership';
import CustomEmoji from '@typings/database/custom_emoji';
import {
DataFactoryArgs,
RawApp,
RawChannel,
RawChannelInfo,
RawChannelMembership,
RawCustomEmoji,
RawDraft,
@@ -21,6 +24,9 @@ import {
RawGroupMembership,
RawGroupsInChannel,
RawGroupsInTeam,
RawMyChannel,
RawMyChannelSettings,
RawMyTeam,
RawPost,
RawPostMetadata,
RawPostsInChannel,
@@ -29,8 +35,12 @@ import {
RawReaction,
RawRole,
RawServers,
RawSlashCommand,
RawSystem,
RawTeam,
RawTeamChannelHistory,
RawTeamMembership,
RawTeamSearchHistory,
RawTermsOfService,
RawUser,
} from '@typings/database/database';
@@ -42,6 +52,9 @@ import Group from '@typings/database/group';
import GroupMembership from '@typings/database/group_membership';
import GroupsInChannel from '@typings/database/groups_in_channel';
import GroupsInTeam from '@typings/database/groups_in_team';
import MyChannel from '@typings/database/my_channel';
import MyChannelSettings from '@typings/database/my_channel_settings';
import MyTeam from '@typings/database/my_team';
import Post from '@typings/database/post';
import PostMetadata from '@typings/database/post_metadata';
import PostsInChannel from '@typings/database/posts_in_channel';
@@ -50,20 +63,30 @@ import Preference from '@typings/database/preference';
import Reaction from '@typings/database/reaction';
import Role from '@typings/database/role';
import Servers from '@typings/database/servers';
import SlashCommand from '@typings/database/slash_command';
import System from '@typings/database/system';
import Team from '@typings/database/team';
import TeamChannelHistory from '@typings/database/team_channel_history';
import TeamMembership from '@typings/database/team_membership';
import TeamSearchHistory from '@typings/database/team_search_history';
import TermsOfService from '@typings/database/terms_of_service';
import User from '@typings/database/user';
const {APP, GLOBAL, SERVERS} = MM_TABLES.DEFAULT;
const {
CHANNEL,
CHANNEL_INFO,
CHANNEL_MEMBERSHIP,
CUSTOM_EMOJI,
DRAFT,
FILE,
GROUP,
GROUPS_IN_TEAM,
GROUPS_IN_CHANNEL,
GROUPS_IN_TEAM,
GROUP_MEMBERSHIP,
MY_CHANNEL,
MY_CHANNEL_SETTINGS,
MY_TEAM,
POST,
POSTS_IN_CHANNEL,
POSTS_IN_THREAD,
@@ -71,14 +94,16 @@ const {
PREFERENCE,
REACTION,
ROLE,
SLASH_COMMAND,
SYSTEM,
TEAM,
TEAM_CHANNEL_HISTORY,
TEAM_MEMBERSHIP,
TEAM_SEARCH_HISTORY,
TERMS_OF_SERVICE,
USER,
} = MM_TABLES.SERVER;
// TODO : Include timezone_count and member_count when you have the information for the group section
/**
* operateAppRecord: Prepares record of entity 'App' from the DEFAULT database for update or create actions.
* @param {DataFactoryArgs} operator
@@ -93,9 +118,9 @@ export const operateAppRecord = async ({action, database, value}: DataFactoryArg
const generator = (app: App) => {
app._raw.id = isCreateAction ? app.id : record.id;
app.buildNumber = raw?.buildNumber;
app.createdAt = raw?.createdAt;
app.versionNumber = raw?.versionNumber;
app.buildNumber = raw?.build_number;
app.createdAt = raw?.created_at;
app.versionNumber = raw?.version_number;
};
return operateBaseRecord({
@@ -148,10 +173,10 @@ export const operateServersRecord = async ({action, database, value}: DataFactor
const generator = (servers: Servers) => {
servers._raw.id = isCreateAction ? servers.id : record.id;
servers.dbPath = raw?.dbPath;
servers.displayName = raw?.displayName;
servers.mentionCount = raw?.mentionCount;
servers.unreadCount = raw?.unreadCount;
servers.dbPath = raw?.db_path;
servers.displayName = raw?.display_name;
servers.mentionCount = raw?.mention_count;
servers.unreadCount = raw?.unread_count;
servers.url = raw?.url;
};
@@ -262,7 +287,7 @@ export const operateTermsOfServiceRecord = async ({action, database, value}: Dat
// id of TOS comes from server response
const generator = (tos: TermsOfService) => {
tos._raw.id = isCreateAction ? (raw?.id ?? tos.id) : record?.id;
tos.acceptedAt = raw?.acceptedAt;
tos.acceptedAt = raw?.accepted_at;
};
return operateBaseRecord({
@@ -688,6 +713,8 @@ export const operateGroupsInTeamRecord = async ({action, database, value}: DataF
const record = value.record as GroupsInTeam;
const isCreateAction = action === OperationType.CREATE;
// FIXME : should include memberCount and timezoneCount or will it be by update action?
const generator = (groupsInTeam: GroupsInTeam) => {
groupsInTeam._raw.id = isCreateAction ? groupsInTeam.id : record?.id;
groupsInTeam.teamId = raw.team_id;
@@ -704,7 +731,7 @@ export const operateGroupsInTeamRecord = async ({action, database, value}: DataF
};
/**
* operateGroupsInChannelRecord: Prepares record of entity 'GROUPS_IN_TEAM' from the SERVER database for update or create actions.
* operateGroupsInChannelRecord: Prepares record of entity 'GROUPS_IN_CHANNEL' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
@@ -715,6 +742,7 @@ export const operateGroupsInChannelRecord = async ({action, database, value}: Da
const record = value.record as GroupsInChannel;
const isCreateAction = action === OperationType.CREATE;
// FIXME : should include memberCount and timezoneCount or will it be by update action?
const generator = (groupsInChannel: GroupsInChannel) => {
groupsInChannel._raw.id = isCreateAction ? groupsInChannel.id : record?.id;
groupsInChannel.channelId = raw.channel_id;
@@ -730,6 +758,284 @@ export const operateGroupsInChannelRecord = async ({action, database, value}: Da
});
};
/**
* operateTeamRecord: Prepares record of entity 'TEAM' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateTeamRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawTeam;
const record = value.record as Team;
const isCreateAction = action === OperationType.CREATE;
// id of team comes from server response
const generator = (team: Team) => {
team._raw.id = isCreateAction ? (raw?.id ?? team.id) : record?.id;
team.isAllowOpenInvite = raw.allow_open_invite;
team.description = raw.description;
team.displayName = raw.display_name;
team.name = raw.name;
team.updateAt = raw.update_at;
team.type = raw.type;
team.allowedDomains = raw.allowed_domains;
team.isGroupConstrained = Boolean(raw.group_constrained);
team.lastTeamIconUpdatedAt = raw.last_team_icon_update;
};
return operateBaseRecord({
action,
database,
tableName: TEAM,
value,
generator,
});
};
/**
* operateTeamChannelHistoryRecord: Prepares record of entity 'TEAM_CHANNEL_HISTORY' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateTeamChannelHistoryRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawTeamChannelHistory;
const record = value.record as TeamChannelHistory;
const isCreateAction = action === OperationType.CREATE;
const generator = (teamChannelHistory: TeamChannelHistory) => {
teamChannelHistory._raw.id = isCreateAction ? (teamChannelHistory.id) : record?.id;
teamChannelHistory.teamId = raw.team_id;
teamChannelHistory.channelIds = raw.channel_ids;
};
return operateBaseRecord({
action,
database,
tableName: TEAM_CHANNEL_HISTORY,
value,
generator,
});
};
/**
* operateTeamSearchHistoryRecord: Prepares record of entity 'TEAM_SEARCH_HISTORY' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateTeamSearchHistoryRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawTeamSearchHistory;
const record = value.record as TeamSearchHistory;
const isCreateAction = action === OperationType.CREATE;
const generator = (teamSearchHistory: TeamSearchHistory) => {
teamSearchHistory._raw.id = isCreateAction ? (teamSearchHistory.id) : record?.id;
teamSearchHistory.createdAt = raw.created_at;
teamSearchHistory.displayTerm = raw.display_term;
teamSearchHistory.term = raw.term;
teamSearchHistory.teamId = raw.team_id;
};
return operateBaseRecord({
action,
database,
tableName: TEAM_SEARCH_HISTORY,
value,
generator,
});
};
/**
* operateSlashCommandRecord: Prepares record of entity 'SLASH_COMMAND' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateSlashCommandRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawSlashCommand;
const record = value.record as SlashCommand;
const isCreateAction = action === OperationType.CREATE;
// id of team comes from server response
const generator = (slashCommand: SlashCommand) => {
slashCommand._raw.id = isCreateAction ? (raw?.id ?? slashCommand.id) : record?.id;
slashCommand.isAutoComplete = raw.auto_complete;
slashCommand.description = raw.description;
slashCommand.displayName = raw.display_name;
slashCommand.hint = raw.auto_complete_hint;
slashCommand.method = raw.method;
slashCommand.teamId = raw.team_id;
slashCommand.token = raw.token;
slashCommand.trigger = raw.trigger;
slashCommand.updateAt = raw.update_at;
};
return operateBaseRecord({
action,
database,
tableName: SLASH_COMMAND,
value,
generator,
});
};
/**
* operateMyTeamRecord: Prepares record of entity 'MY_TEAM' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateMyTeamRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawMyTeam;
const record = value.record as MyTeam;
const isCreateAction = action === OperationType.CREATE;
const generator = (myTeam: MyTeam) => {
myTeam._raw.id = isCreateAction ? myTeam.id : record?.id;
myTeam.teamId = raw.team_id;
myTeam.roles = raw.roles;
myTeam.isUnread = raw.is_unread;
myTeam.mentionsCount = raw.mentions_count;
};
return operateBaseRecord({
action,
database,
tableName: MY_TEAM,
value,
generator,
});
};
/**
* operateChannelRecord: Prepares record of entity 'CHANNEL' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateChannelRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawChannel;
const record = value.record as Channel;
const isCreateAction = action === OperationType.CREATE;
// id of team comes from server response
const generator = (channel: Channel) => {
channel._raw.id = isCreateAction ? (raw?.id ?? channel.id) : record?.id;
channel.createAt = raw.create_at;
channel.creatorId = raw.creator_id;
channel.deleteAt = raw.delete_at;
channel.displayName = raw.display_name;
channel.isGroupConstrained = Boolean(raw.group_constrained);
channel.name = raw.name;
channel.teamId = raw.team_id;
channel.type = raw.type;
};
return operateBaseRecord({
action,
database,
tableName: CHANNEL,
value,
generator,
});
};
/**
* operateMyChannelSettingsRecord: Prepares record of entity 'MY_CHANNEL_SETTINGS' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateMyChannelSettingsRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawMyChannelSettings;
const record = value.record as MyChannelSettings;
const isCreateAction = action === OperationType.CREATE;
const generator = (myChannelSetting: MyChannelSettings) => {
myChannelSetting._raw.id = isCreateAction ? myChannelSetting.id : record?.id;
myChannelSetting.channelId = raw.channel_id;
myChannelSetting.notifyProps = raw.notify_props;
};
return operateBaseRecord({
action,
database,
tableName: MY_CHANNEL_SETTINGS,
value,
generator,
});
};
/**
* operateChannelInfoRecord: Prepares record of entity 'CHANNEL_INFO' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateChannelInfoRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawChannelInfo;
const record = value.record as ChannelInfo;
const isCreateAction = action === OperationType.CREATE;
const generator = (channelInfo: ChannelInfo) => {
channelInfo._raw.id = isCreateAction ? channelInfo.id : record?.id;
channelInfo.channelId = raw.channel_id;
channelInfo.guestCount = raw.guest_count;
channelInfo.header = raw.header;
channelInfo.memberCount = raw.member_count;
channelInfo.pinned_post_count = raw.pinned_post_count;
channelInfo.purpose = raw.purpose;
};
return operateBaseRecord({
action,
database,
tableName: CHANNEL_INFO,
value,
generator,
});
};
/**
* operateMyChannelRecord: Prepares record of entity 'MY_CHANNEL' from the SERVER database for update or create actions.
* @param {DataFactory} operator
* @param {Database} operator.database
* @param {MatchExistingRecord} operator.value
* @returns {Promise<Model>}
*/
export const operateMyChannelRecord = async ({action, database, value}: DataFactoryArgs) => {
const raw = value.raw as RawMyChannel;
const record = value.record as MyChannel;
const isCreateAction = action === OperationType.CREATE;
const generator = (myChannel: MyChannel) => {
myChannel._raw.id = isCreateAction ? myChannel.id : record?.id;
myChannel.channelId = raw.channel_id;
myChannel.roles = raw.roles;
myChannel.messageCount = raw.message_count;
myChannel.mentionsCount = raw.mentions_count;
myChannel.lastPostAt = raw.last_post_at;
myChannel.lastViewedAt = raw.last_viewed_at;
};
return operateBaseRecord({
action,
database,
tableName: MY_CHANNEL,
value,
generator,
});
};
/**
* operateBaseRecord: This is the last step for each operator and depending on the 'action', it will either prepare an
* existing record for UPDATE or prepare a collection for CREATE

View File

@@ -6,7 +6,9 @@ import {DatabaseType, OperationType} from '@typings/database/enums';
import {
operateAppRecord,
operateChannelInfoRecord,
operateChannelMembershipRecord,
operateChannelRecord,
operateCustomEmojiRecord,
operateDraftRecord,
operateFileRecord,
@@ -15,6 +17,9 @@ import {
operateGroupRecord,
operateGroupsInChannelRecord,
operateGroupsInTeamRecord,
operateMyChannelRecord,
operateMyChannelSettingsRecord,
operateMyTeamRecord,
operatePostInThreadRecord,
operatePostMetadataRecord,
operatePostRecord,
@@ -23,8 +28,12 @@ import {
operateReactionRecord,
operateRoleRecord,
operateServersRecord,
operateSlashCommandRecord,
operateSystemRecord,
operateTeamChannelHistoryRecord,
operateTeamMembershipRecord,
operateTeamRecord,
operateTeamSearchHistoryRecord,
operateTermsOfServiceRecord,
operateUserRecord,
} from './index';
@@ -69,16 +78,15 @@ describe('*** DataOperator: Operators tests ***', () => {
value: {
record: undefined,
raw: {
buildNumber: 'build-7',
createdAt: 1,
id: 'id-18',
versionNumber: 'v-1',
build_number: 'build-7',
created_at: 1,
version_number: 'v-1',
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('App');
expect(preparedRecords!.collection.modelClass.name).toBe('App');
});
it('=> operateGlobalRecord: should return an array of type Global', async () => {
@@ -92,12 +100,12 @@ describe('*** DataOperator: Operators tests ***', () => {
database: database!,
value: {
record: undefined,
raw: {id: 'g-1', name: 'g-n1', value: 'g-v1'},
raw: {name: 'g-n1', value: 'g-v1'},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Global');
expect(preparedRecords!.collection.modelClass.name).toBe('Global');
});
it('=> operateServersRecord: should return an array of type Servers', async () => {
@@ -112,18 +120,17 @@ describe('*** DataOperator: Operators tests ***', () => {
value: {
record: undefined,
raw: {
dbPath: 'mm-server',
displayName: 's-displayName',
id: 's-1',
mentionCount: 1,
unreadCount: 0,
db_path: 'mm-server',
display_name: 's-displayName',
mention_count: 1,
unread_count: 0,
url: 'https://community.mattermost.com',
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Servers');
expect(preparedRecords!.collection.modelClass.name).toBe('Servers');
});
it('=> operateRoleRecord: should return an array of type Role', async () => {
@@ -146,7 +153,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Role');
expect(preparedRecords!.collection.modelClass.name).toBe('Role');
});
it('=> operateSystemRecord: should return an array of type System', async () => {
@@ -165,7 +172,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('System');
expect(preparedRecords!.collection.modelClass.name).toBe('System');
});
it('=> operateTermsOfServiceRecord: should return an array of type TermsOfService', async () => {
@@ -181,7 +188,7 @@ describe('*** DataOperator: Operators tests ***', () => {
record: undefined,
raw: {
id: 'tos-1',
acceptedAt: 1,
accepted_at: 1,
create_at: 1613667352029,
user_id: 'user1613667352029',
text: '',
@@ -190,7 +197,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch(
expect(preparedRecords!.collection.modelClass.name).toBe(
'TermsOfService',
);
});
@@ -232,7 +239,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Post');
expect(preparedRecords!.collection.modelClass.name).toBe('Post');
});
it('=> operatePostInThreadRecord: should return an array of type PostsInThread', async () => {
@@ -256,7 +263,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch(
expect(preparedRecords!.collection.modelClass.name).toBe(
'PostsInThread',
);
});
@@ -285,7 +292,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Reaction');
expect(preparedRecords!.collection.modelClass.name).toBe('Reaction');
});
it('=> operateFileRecord: should return an array of type File', async () => {
@@ -314,7 +321,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('File');
expect(preparedRecords!.collection.modelClass.name).toBe('File');
});
it('=> operatePostMetadataRecord: should return an array of type PostMetadata', async () => {
@@ -338,7 +345,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('PostMetadata');
expect(preparedRecords!.collection.modelClass.name).toBe('PostMetadata');
});
it('=> operateDraftRecord: should return an array of type Draft', async () => {
@@ -363,7 +370,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Draft');
expect(preparedRecords!.collection.modelClass.name).toBe('Draft');
});
it('=> operatePostsInChannelRecord: should return an array of type PostsInChannel', async () => {
@@ -387,7 +394,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch(
expect(preparedRecords!.collection.modelClass.name).toBe(
'PostsInChannel',
);
});
@@ -446,7 +453,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('User');
expect(preparedRecords!.collection.modelClass.name).toBe('User');
});
it('=> operatePreferenceRecord: should return an array of type Preference', async () => {
@@ -465,10 +472,10 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Preference');
expect(preparedRecords!.collection.modelClass.name).toBe('Preference');
});
it('=> operatePreferenceRecord: should return an array of type TEAM_MEMBERSHIP', async () => {
it('=> operateTeamMembershipRecord: should return an array of type TeamMembership', async () => {
expect.assertions(3);
const database = await createConnection();
@@ -493,7 +500,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('TeamMembership');
expect(preparedRecords!.collection.modelClass.name).toBe('TeamMembership');
});
it('=> operateCustomEmojiRecord: should return an array of type CustomEmoji', async () => {
@@ -519,7 +526,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('CustomEmoji');
expect(preparedRecords!.collection.modelClass.name).toBe('CustomEmoji');
});
it('=> operateGroupMembershipRecord: should return an array of type GroupMembership', async () => {
@@ -542,7 +549,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('GroupMembership');
expect(preparedRecords!.collection.modelClass.name).toBe('GroupMembership');
});
it('=> operateChannelMembershipRecord: should return an array of type ChannelMembership', async () => {
@@ -580,7 +587,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('ChannelMembership');
expect(preparedRecords!.collection.modelClass.name).toBe('ChannelMembership');
});
it('=> operateGroupRecord: should return an array of type Group', async () => {
@@ -610,7 +617,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('Group');
expect(preparedRecords!.collection.modelClass.name).toBe('Group');
});
it('=> operateGroupsInTeamRecord: should return an array of type GroupsInTeam', async () => {
@@ -638,7 +645,7 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('GroupsInTeam');
expect(preparedRecords!.collection.modelClass.name).toBe('GroupsInTeam');
});
it('=> operateGroupsInChannelRecord: should return an array of type GroupsInChannel', async () => {
@@ -669,6 +676,269 @@ describe('*** DataOperator: Operators tests ***', () => {
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toMatch('GroupsInChannel');
expect(preparedRecords!.collection.modelClass.name).toBe('GroupsInChannel');
});
it('=> operateTeamRecord: should return an array of type Team', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateTeamRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
id: 'rcgiyftm7jyrxnmdfdfa1osd8zswby',
create_at: 1445538153952,
update_at: 1588876392150,
delete_at: 0,
display_name: 'Contributors',
name: 'core',
description: '',
email: '',
type: 'O',
company_name: '',
allowed_domains: '',
invite_id: 'codoy5s743rq5mk18i7u5dfdfksz7e',
allow_open_invite: true,
last_team_icon_update: 1525181587639,
scheme_id: 'hbwgrncq1pfcdkpotzidfdmarn95o',
group_constrained: null,
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('Team');
});
it('=> operateTeamChannelHistoryRecord: should return an array of type Team', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateTeamChannelHistoryRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
team_id: 'a',
channel_ids: ['ca', 'cb'],
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('TeamChannelHistory');
});
it('=> operateTeamSearchHistoryRecord: should return an array of type TeamSearchHistory', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateTeamSearchHistoryRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
team_id: 'a',
term: 'termA',
display_term: 'termA',
created_at: 1445538153952,
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('TeamSearchHistory');
});
it('=> operateSlashCommandRecord: should return an array of type SlashCommand', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateSlashCommandRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
id: 'command_1',
auto_complete: true,
auto_complete_desc: 'mock_command',
auto_complete_hint: 'hint',
create_at: 1445538153952,
creator_id: 'creator_id',
delete_at: 1445538153952,
description: 'description',
display_name: 'display_name',
icon_url: 'display_name',
method: 'get',
team_id: 'teamA',
token: 'token',
trigger: 'trigger',
update_at: 1445538153953,
url: 'url',
username: 'userA',
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('SlashCommand');
});
it('=> operateMyTeamRecord: should return an array of type MyTeam', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateMyTeamRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
team_id: 'teamA',
roles: 'roleA, roleB, roleC',
is_unread: true,
mentions_count: 3,
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('MyTeam');
});
it('=> operateChannelRecord: should return an array of type Channel', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateChannelRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
id: 'kow9j1ttnxwig7tnqgebg7dtipno',
create_at: 1600185541285,
update_at: 1604401077256,
delete_at: 0,
team_id: '',
type: 'D',
display_name: '',
name: 'jui1zkzkhh357b4bejephjz5u8daw__9ciscaqbrpd6d8s68k76xb9bte',
header: 'https://mattermost)',
purpose: '',
last_post_at: 1617311494451,
total_msg_count: 585,
extra_update_at: 0,
creator_id: '',
scheme_id: null,
props: null,
group_constrained: null,
shared: null,
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('Channel');
});
it('=> operateMyChannelSettingsRecord: should return an array of type MyChannelSettings', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateMyChannelSettingsRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
channel_id: 'c',
notify_props: {
desktop: 'all',
desktop_sound: true,
email: true,
first_name: true,
mention_keys: '',
push: 'mention',
channel: true,
},
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('MyChannelSettings');
});
it('=> operateChannelInfoRecord: should return an array of type ChannelInfo', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateChannelInfoRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
channel_id: 'c',
guest_count: 10,
header: 'channel info header',
member_count: 10,
pinned_post_count: 3,
purpose: 'sample channel ',
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('ChannelInfo');
});
it('=> operateMyChannelRecord: should return an array of type MyChannel', async () => {
expect.assertions(3);
const database = await createConnection();
expect(database).toBeTruthy();
const preparedRecords = await operateMyChannelRecord({
action: OperationType.CREATE,
database: database!,
value: {
record: undefined,
raw: {
channel_id: 'cd',
last_post_at: 1617311494451,
last_viewed_at: 1617311494451,
mentions_count: 3,
message_count: 10,
roles: 'guest',
},
},
});
expect(preparedRecords).toBeTruthy();
expect(preparedRecords!.collection.modelClass.name).toBe('MyChannel');
});
});

View File

@@ -5,14 +5,19 @@ import {Q} from '@nozbe/watermelondb';
import Model from '@nozbe/watermelondb/Model';
import {MM_TABLES} from '@constants/database';
import Channel from '@typings/database/channel';
import {
ChainPostsArgs,
IdenticalRecordArgs,
MatchExistingRecord,
RangeOfValueArgs,
RawChannel,
RawPost,
RawReaction,
RawUser, RawValue,
RawSlashCommand,
RawTeam,
RawUser,
RawValue,
RecordPair,
RetrieveRecordsArgs,
SanitizePostsArgs,
@@ -20,9 +25,11 @@ import {
} from '@typings/database/database';
import Reaction from '@typings/database/reaction';
import Post from '@typings/database/post';
import {User} from '@database/server/models';
import SlashCommand from '@typings/database/slash_command';
import Team from '@typings/database/team';
import User from '@typings/database/user';
const {POST, USER, REACTION} = MM_TABLES.SERVER;
const {CHANNEL, POST, REACTION, SLASH_COMMAND, TEAM, USER} = MM_TABLES.SERVER;
/**
* sanitizePosts: Creates arrays of ordered and unordered posts. Unordered posts are those posts that are not
@@ -161,11 +168,11 @@ export const retrieveRecords = async ({database, tableName, condition}: Retrieve
* @returns {boolean}
*/
export const hasSimilarUpdateAt = ({tableName, newValue, existingRecord}: IdenticalRecordArgs) => {
const guardTables = [POST, USER];
const guardTables = [CHANNEL, POST, SLASH_COMMAND, TEAM, USER];
if (guardTables.includes(tableName)) {
type Raw = RawPost | RawUser
type ExistingRecord = Post | User
type Raw = RawPost | RawUser | RawTeam | RawSlashCommand | RawChannel
type ExistingRecord = Post | User | Team | SlashCommand | Channel
return (newValue as Raw).update_at === (existingRecord as ExistingRecord).updateAt;
}
@@ -200,3 +207,18 @@ export const getRawRecordPairs = (raws: any[]): RecordPair[] => {
return {raw, record: undefined};
});
};
/**
* getUniqueRawsBy: We have to ensure that we are not updating the same record twice in the same operation.
* Hence, thought it might not occur, prevention is better than cure. This function removes duplicates from the 'raws' array.
* @param {RawValue[]} raws
* @param {string} key
*/
export const getUniqueRawsBy = ({raws, key}:{ raws: RawValue[], key: string}) => {
return [...new Map(raws.map((item) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const curItemKey = item[key];
return [curItemKey, item];
})).values()];
};

View File

@@ -167,7 +167,7 @@ describe('*** Database Manager tests ***', () => {
const occurrences = allServers?.map((server) => server.url).reduce((acc, cur) => (cur === serverUrl ? acc + 1 : acc), 0);
// We should only have one occurence of the 'https://appv3.mattermost.com' url
// We should only have one occurrence of the 'https://appv3.mattermost.com' url
expect(occurrences).toEqual(1);
});
});

View File

@@ -10,7 +10,7 @@ const {SERVERS} = MM_TABLES.DEFAULT;
/**
* The Server model will help us to identify the various servers a user will log in; in the context of
* multi-server support system. The dbPath field will hold the App-Groups file-path
* multi-server support system. The db_path field will hold the App-Groups file-path
*/
export default class Servers extends Model {
/** table (entity name) : servers */

View File

@@ -78,6 +78,9 @@ export default class Channel extends Model {
/** creator_id : The user who created this channel */
@field('creator_id') creatorId!: string;
/** update_at : The timestamp to when this channel was last updated on the server */
@field('update_at') updateAt!: number;
/** delete_at : The deletion/archived date of this channel */
@field('delete_at') deleteAt!: number;

View File

@@ -30,7 +30,7 @@ export default class MyTeam extends Model {
/** mentions_count : Count of posts in which the user has been mentioned */
@field('mentions_count') mentionsCount!: number;
/** roles : The different permissions that this user has in the team */
/** roles : The different permissions that this user has in the team, concatenated together with comma to form a single string. */
@field('roles') roles!: string;
/** team_id : The foreign key of the 'parent' Team entity */

View File

@@ -48,6 +48,9 @@ export default class SlashCommand extends Model {
/** trigger : A pattern/text used to recognize when a slash command needs to launch */
@field('trigger') trigger!: string;
/** update_at : The timestamp to when this command was last updated on the server */
@field('update_at') updateAt!: number;
/** team : The related parent TEAM record */
@immutableRelation(TEAM, 'team_id') team!: Relation<Team>;
}

View File

@@ -75,6 +75,9 @@ export default class Team extends Model {
/** name : The name for the team */
@field('name') name!: string;
/** update_at : The timestamp to when this team was last updated on the server */
@field('update_at') updateAt!: number;
/** type : The type of team ( e.g. open/private ) */
@field('type') type!: string;

View File

@@ -18,5 +18,6 @@ export default tableSchema({
{name: 'name', type: 'string', isIndexed: true},
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'type', type: 'string'},
{name: 'update_at', type: 'number'},
],
});

View File

@@ -18,5 +18,6 @@ export default tableSchema({
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'token', type: 'string'},
{name: 'trigger', type: 'string'},
{name: 'update_at', type: 'number'},
],
});

View File

@@ -18,5 +18,6 @@ export default tableSchema({
{name: 'last_team_icon_updated_at', type: 'number'},
{name: 'name', type: 'string'},
{name: 'type', type: 'string'},
{name: 'update_at', type: 'number'},
],
});

View File

@@ -67,10 +67,15 @@ describe('*** Test schema for SERVER database ***', () => {
creator_id: {name: 'creator_id', type: 'string', isIndexed: true},
delete_at: {name: 'delete_at', type: 'number'},
display_name: {name: 'display_name', type: 'string'},
is_group_constrained: {name: 'is_group_constrained', type: 'boolean'},
is_group_constrained: {
name: 'is_group_constrained',
type: 'boolean',
},
name: {name: 'name', type: 'string', isIndexed: true},
team_id: {name: 'team_id', type: 'string', isIndexed: true},
type: {name: 'type', type: 'string'},
update_at: {name: 'update_at', type: 'number'},
},
columnArray: [
{name: 'create_at', type: 'number'},
@@ -81,6 +86,7 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'name', type: 'string', isIndexed: true},
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'type', type: 'string'},
{name: 'update_at', type: 'number'},
],
},
[CHANNEL_MEMBERSHIP]: {
@@ -99,9 +105,7 @@ describe('*** Test schema for SERVER database ***', () => {
columns: {
name: {name: 'name', type: 'string'},
},
columnArray: [
{name: 'name', type: 'string'},
],
columnArray: [{name: 'name', type: 'string'}],
},
[MY_CHANNEL]: {
name: MY_CHANNEL,
@@ -144,7 +148,6 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'channel_id', type: 'string', isIndexed: true},
{name: 'earliest', type: 'number'},
{name: 'latest', type: 'number'},
],
},
[DRAFT]: {
@@ -367,6 +370,7 @@ describe('*** Test schema for SERVER database ***', () => {
team_id: {name: 'team_id', type: 'string', isIndexed: true},
token: {name: 'token', type: 'string'},
trigger: {name: 'trigger', type: 'string'},
update_at: {name: 'update_at', type: 'number'},
},
columnArray: [
{name: 'is_auto_complete', type: 'boolean'},
@@ -377,6 +381,7 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'team_id', type: 'string', isIndexed: true},
{name: 'token', type: 'string'},
{name: 'trigger', type: 'string'},
{name: 'update_at', type: 'number'},
],
},
[SYSTEM]: {
@@ -393,14 +398,24 @@ describe('*** Test schema for SERVER database ***', () => {
[TEAM]: {
name: TEAM,
columns: {
is_allow_open_invite: {name: 'is_allow_open_invite', type: 'boolean'},
is_allow_open_invite: {
name: 'is_allow_open_invite',
type: 'boolean',
},
allowed_domains: {name: 'allowed_domains', type: 'string'},
description: {name: 'description', type: 'string'},
display_name: {name: 'display_name', type: 'string'},
is_group_constrained: {name: 'is_group_constrained', type: 'boolean'},
last_team_icon_updated_at: {name: 'last_team_icon_updated_at', type: 'number'},
is_group_constrained: {
name: 'is_group_constrained',
type: 'boolean',
},
last_team_icon_updated_at: {
name: 'last_team_icon_updated_at',
type: 'number',
},
name: {name: 'name', type: 'string'},
type: {name: 'type', type: 'string'},
update_at: {name: 'update_at', type: 'number'},
},
columnArray: [
{name: 'is_allow_open_invite', type: 'boolean'},
@@ -411,6 +426,7 @@ describe('*** Test schema for SERVER database ***', () => {
{name: 'last_team_icon_updated_at', type: 'number'},
{name: 'name', type: 'string'},
{name: 'type', type: 'string'},
{name: 'update_at', type: 'number'},
],
},
[TEAM_CHANNEL_HISTORY]: {
@@ -442,7 +458,6 @@ describe('*** Test schema for SERVER database ***', () => {
display_term: {name: 'display_term', type: 'string'},
team_id: {name: 'team_id', type: 'string', isIndexed: true},
term: {name: 'term', type: 'string'},
},
columnArray: [
{name: 'created_at', type: 'number'},
@@ -456,9 +471,7 @@ describe('*** Test schema for SERVER database ***', () => {
columns: {
accepted_at: {name: 'accepted_at', type: 'number'},
},
columnArray: [
{name: 'accepted_at', type: 'number'},
],
columnArray: [{name: 'accepted_at', type: 'number'}],
},
[USER]: {
name: USER,
@@ -471,7 +484,10 @@ describe('*** Test schema for SERVER database ***', () => {
is_bot: {name: 'is_bot', type: 'boolean'},
is_guest: {name: 'is_guest', type: 'boolean'},
last_name: {name: 'last_name', type: 'string'},
last_picture_update: {name: 'last_picture_update', type: 'number'},
last_picture_update: {
name: 'last_picture_update',
type: 'number',
},
locale: {name: 'locale', type: 'string'},
nickname: {name: 'nickname', type: 'string'},
notify_props: {name: 'notify_props', type: 'string'},

View File

@@ -34,6 +34,9 @@ export default class Channel extends Model {
/** delete_at : The deletion/archived date of this channel */
deleteAt: number;
/** update_at : The timestamp to when this channel was last updated on the server */
updateAt!: number;
/** display_name : The channel display name (e.g. Town Square ) */
displayName: string;

View File

@@ -31,24 +31,21 @@ export type DefaultNewServerArgs = {
export type DatabaseInstance = Database | undefined;
export type RawApp = {
buildNumber: string;
createdAt: number;
id: string;
versionNumber: string;
build_number: string;
created_at: number;
version_number: string;
};
export type RawGlobal = {
id: string;
name: string;
value: string;
};
export type RawServers = {
dbPath: string;
displayName: string;
id: string;
mentionCount: number;
unreadCount: number;
db_path: string;
display_name: string;
mention_count: number;
unread_count: number;
url: string;
};
@@ -78,7 +75,7 @@ export type RawSystem = {
export type RawTermsOfService = {
id: string;
acceptedAt: number;
accepted_at: number;
create_at: number;
user_id: string;
text: string;
@@ -191,8 +188,6 @@ export type RawPost = {
};
};
export type ChannelType = 'D' | 'O' | 'G' | 'P';
export type RawUser = {
id: string;
auth_service: string;
@@ -296,6 +291,110 @@ export type RawChannelMembers = {
user_id: string;
};
export type RawPostsInThread = {
earliest: number;
latest?: number;
post_id: string;
};
export type RawGroup = {
create_at: number;
delete_at: number;
description: string;
display_name: string;
has_syncables: boolean;
id: string;
name: string;
remote_id: string;
source: string;
update_at: number;
};
export type RawGroupsInTeam = {
auto_add: boolean;
create_at: number;
delete_at: number;
group_id: string;
team_display_name: string;
team_id: string;
team_type: string;
update_at: number;
};
export type RawGroupsInChannel = {
auto_add: boolean;
channel_display_name: string;
channel_id: string;
channel_type: string;
create_at: number;
delete_at: number;
group_id: string;
team_display_name: string;
team_id: string;
team_type: string;
update_at: number;
};
export type RawTeam = {
id: string;
allow_open_invite: boolean;
allowed_domains: string;
company_name: string;
create_at: number;
delete_at: number;
description: string;
display_name: string;
email: string;
group_constrained: boolean | null;
invite_id: string;
last_team_icon_update: number;
name: string;
scheme_id: string;
type: string;
update_at: number;
};
export type RawTeamChannelHistory = {
team_id: string;
channel_ids: string[]
}
export type RawTeamSearchHistory = {
created_at: number;
display_term: string;
term: string;
team_id: string;
}
export type RawSlashCommand = {
id: string;
auto_complete: boolean;
auto_complete_desc: string;
auto_complete_hint: string;
create_at: number;
creator_id: string;
delete_at: number;
description: string;
display_name: string;
icon_url: string;
method: string;
team_id: string;
token: string;
trigger: string;
update_at: number;
url: string;
username: string;
};
export type RawMyTeam = {
team_id: string;
roles: string;
is_unread: boolean;
mentions_count: number;
};
export type ChannelType = 'D' | 'O' | 'G' | 'P';
export type RawChannel = {
create_at: number;
creator_id: string;
@@ -317,52 +416,33 @@ export type RawChannel = {
update_at: number;
};
export type RawPostsInThread = {
earliest: number;
latest?: number;
post_id: string;
};
export type RawGroup = {
create_at: number,
delete_at: number,
description: string,
display_name: string,
has_syncables: boolean
id: string,
name: string,
remote_id: string,
source: string,
update_at: number,
export type RawMyChannelSettings = {
notify_props: NotifyProps,
channel_id: string;
}
export type RawGroupsInTeam = {
auto_add: boolean,
create_at: number,
delete_at: number,
group_id: string,
team_display_name: string,
team_id: string,
team_type: string,
update_at: number
export type RawChannelInfo = {
channel_id: string;
guest_count: number;
header: string;
member_count: number;
pinned_post_count: number;
purpose: string;
}
export type RawGroupsInChannel = {
auto_add: boolean,
channel_display_name: string,
channel_id: string,
channel_type: string,
create_at: number,
delete_at: number,
group_id: string,
team_display_name: string,
team_id: string,
team_type: string,
update_at: number
export type RawMyChannel = {
channel_id: string;
last_post_at: number;
last_viewed_at: number;
mentions_count: number;
message_count: number;
roles: string;
}
export type RawValue =
| RawApp
| RawChannel
| RawChannelInfo
| RawChannelMembership
| RawCustomEmoji
| RawDraft
@@ -372,6 +452,9 @@ export type RawValue =
| RawGroupMembership
| RawGroupsInChannel
| RawGroupsInTeam
| RawMyChannel
| RawMyChannelSettings
| RawMyTeam
| RawPost
| RawPostMetadata
| RawPostsInChannel
@@ -380,8 +463,12 @@ export type RawValue =
| RawReaction
| RawRole
| RawServers
| RawSlashCommand
| RawSystem
| RawTeam
| RawTeamChannelHistory
| RawTeamMembership
| RawTeamSearchHistory
| RawTermsOfService
| RawUser;
@@ -402,7 +489,9 @@ export type PrepareForDatabaseArgs = {
recordOperator: (DataFactoryArgs) => void;
};
export type PrepareRecordsArgs = PrepareForDatabaseArgs & { database: Database; };
export type PrepareRecordsArgs = PrepareForDatabaseArgs & {
database: Database;
};
export type BatchOperationsArgs = { database: Database; models: Model[] };
@@ -420,7 +509,10 @@ export type DatabaseConnectionArgs = {
};
// The elements required to switch to another active server database
export type ActiveServerDatabaseArgs = { displayName: string; serverUrl: string };
export type ActiveServerDatabaseArgs = {
displayName: string;
serverUrl: string;
};
export type HandleReactionsArgs = {
prepareRowsOnly: boolean;
@@ -477,11 +569,11 @@ export type ProcessInputsArgs = {
rawValues: RawValue[];
tableName: string;
fieldName: string;
comparator: (existing: Model, newElement: RawValue) => boolean;
findMatchingRecordBy: (existing: Model, newElement: RawValue) => boolean;
};
export type HandleEntityRecordsArgs = {
comparator: (existing: Model, newElement: RawValue) => boolean;
findMatchingRecordBy: (existing: Model, newElement: RawValue) => boolean;
fieldName: string;
operator: (DataFactoryArgs) => Promise<Model | null>;
rawValues: RawValue[];
@@ -501,4 +593,4 @@ export type RangeOfValueArgs = {
export type RecordPair = {
record?: Model;
raw: RawValue;
}
};

View File

@@ -22,7 +22,7 @@ export default class MyTeam extends Model {
/** mentions_count : Count of posts in which the user has been mentioned */
mentionsCount: number;
/** roles : The different permissions that this user has in the team */
/** roles : The different permissions that this user has in the team, concatenated together with comma to form a single string. */
roles: string;
/** team_id : The foreign key of the 'parent' Team entity */

View File

@@ -5,7 +5,7 @@ import {Model} from '@nozbe/watermelondb';
/**
* The Server model will help us to identify the various servers a user will log in; in the context of
* multi-server support system. The dbPath field will hold the App-Groups file-path
* multi-server support system. The db_path field will hold the App-Groups file-path
*/
export default class Servers extends Model {
/** table (entity name) : servers */

View File

@@ -40,6 +40,9 @@ export default class SlashCommand extends Model {
/** trigger : A pattern/text used to recognize when a slash command needs to launch */
trigger: string;
/** update_at : The timestamp to when this command was last updated on the server */
updateAt!: number;
/** team : The related parent TEAM record */
team: Relation<Team>;
}

View File

@@ -31,6 +31,9 @@ export default class Team extends Model {
/** display_name : The display name for the team */
displayName: string;
/** update_at : The timestamp to when this team was last updated on the server */
updateAt!: number;
/** is_group_constrained : Boolean flag indicating if members are managed groups */
isGroupConstrained: boolean;