forked from Ivasoft/mattermost-mobile
[Gekidou HOTFIX] fixes saving threads in the DB (#6132)
* [Gekidou HOTFIX] fixes saving threads in the DB `loadedInGlobalThreads` cannot be a property of the thread, the DB will fail with unknown column upon saving. This commit makes it an argument of handleThreads and handleThreadInTeam. Fixes wrong foreignKey in THREAD table associations. * Fixes errors * Fixes error
This commit is contained in:
@@ -140,7 +140,7 @@ export const createThreadFromNewPost = async (serverUrl: string, post: Post, pre
|
||||
};
|
||||
|
||||
// On receiving threads, Along with the "threads" & "thread participants", extract and save "posts" & "users"
|
||||
export const processReceivedThreads = async (serverUrl: string, threads: Thread[], teamId: string, prepareRecordsOnly = false) => {
|
||||
export const processReceivedThreads = async (serverUrl: string, threads: Thread[], teamId: string, prepareRecordsOnly = false, loadedInGlobalThreads = false) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
@@ -173,6 +173,7 @@ export const processReceivedThreads = async (serverUrl: string, threads: Thread[
|
||||
threads,
|
||||
teamId,
|
||||
prepareRecordsOnly: true,
|
||||
loadedInGlobalThreads,
|
||||
});
|
||||
|
||||
if (threadModels.length) {
|
||||
|
||||
@@ -100,7 +100,7 @@ export const fetchThreads = async (
|
||||
thread.is_following = true;
|
||||
});
|
||||
|
||||
await processReceivedThreads(serverUrl, threads, teamId);
|
||||
await processReceivedThreads(serverUrl, threads, teamId, false, !unread);
|
||||
}
|
||||
|
||||
return {data};
|
||||
@@ -121,7 +121,7 @@ export const fetchThread = async (serverUrl: string, teamId: string, threadId: s
|
||||
try {
|
||||
const thread = await client.getThread('me', teamId, threadId, extended);
|
||||
|
||||
await processReceivedThreads(serverUrl, [thread], teamId);
|
||||
await processReceivedThreads(serverUrl, [thread], teamId, false, false);
|
||||
|
||||
return {data: thread};
|
||||
} catch (error) {
|
||||
|
||||
@@ -106,15 +106,23 @@ export default class TeamModel extends Model implements TeamModelInterface {
|
||||
@lazy threadsList = this.collections.get<ThreadModel>(THREAD).query(
|
||||
Q.on(THREADS_IN_TEAM, Q.and(
|
||||
Q.where('team_id', this.id),
|
||||
Q.where('loadedInGlobalThreads', true),
|
||||
Q.where('loaded_in_global_threads', true),
|
||||
)),
|
||||
Q.and(
|
||||
Q.where('reply_count', Q.gt(0)),
|
||||
Q.where('is_following', true),
|
||||
),
|
||||
Q.sortBy('last_reply_at', Q.desc),
|
||||
);
|
||||
|
||||
/** unreadThreadsList : Unread threads list belonging to a team */
|
||||
@lazy unreadThreadsList = this.collections.get<ThreadModel>(THREAD).query(
|
||||
Q.on(THREADS_IN_TEAM, 'team_id', this.id),
|
||||
Q.where('unread_replies', Q.gt(0)),
|
||||
Q.and(
|
||||
Q.where('reply_count', Q.gt(0)),
|
||||
Q.where('is_following', true),
|
||||
Q.where('unread_replies', Q.gt(0)),
|
||||
),
|
||||
Q.sortBy('last_reply_at', Q.desc),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export default class ThreadModel extends Model implements ThreadModelInterface {
|
||||
[THREAD_PARTICIPANT]: {type: 'has_many', foreignKey: 'thread_id'},
|
||||
|
||||
/** A THREAD can have multiple THREADS_IN_TEAM. (relationship is 1:N)*/
|
||||
[THREADS_IN_TEAM]: {type: 'has_many', foreignKey: 'team_id'},
|
||||
[THREADS_IN_TEAM]: {type: 'has_many', foreignKey: 'thread_id'},
|
||||
};
|
||||
|
||||
/** last_reply_at : The timestamp of when user last replied to the thread. */
|
||||
|
||||
@@ -28,7 +28,7 @@ export default class ThreadInTeamModel extends Model implements ThreadInTeamMode
|
||||
[TEAM]: {type: 'belongs_to', key: 'team_id'},
|
||||
|
||||
/** A THREAD can have many THREADS_IN_TEAM. (relationship is N:1)*/
|
||||
[THREAD]: {type: 'belongs_to', key: 'team_id'},
|
||||
[THREAD]: {type: 'belongs_to', key: 'thread_id'},
|
||||
};
|
||||
|
||||
/** thread_id: Associated thread identifier */
|
||||
|
||||
@@ -46,12 +46,11 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
||||
is_following: true,
|
||||
unread_replies: 0,
|
||||
unread_mentions: 0,
|
||||
loaded_in_global_threads: false,
|
||||
},
|
||||
] as Thread[];
|
||||
|
||||
const threadsMap = {team_id_1: threads};
|
||||
await operator.handleThreads({threads, prepareRecordsOnly: false, teamId: 'team_id_1'});
|
||||
await operator.handleThreads({threads, loadedInGlobalThreads: false, prepareRecordsOnly: false, teamId: 'team_id_1'});
|
||||
|
||||
expect(spyOnHandleRecords).toHaveBeenCalledWith({
|
||||
fieldName: 'id',
|
||||
@@ -76,6 +75,7 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
||||
expect(spyOnHandleThreadInTeam).toHaveBeenCalledWith({
|
||||
threadsMap,
|
||||
prepareRecordsOnly: true,
|
||||
loadedInGlobalThreads: false,
|
||||
});
|
||||
|
||||
// Only one batch operation for both tables
|
||||
@@ -125,7 +125,6 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
||||
is_following: true,
|
||||
unread_replies: 0,
|
||||
unread_mentions: 0,
|
||||
loaded_in_global_threads: true,
|
||||
},
|
||||
{
|
||||
id: 'thread-2',
|
||||
@@ -138,7 +137,6 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
||||
is_following: true,
|
||||
unread_replies: 0,
|
||||
unread_mentions: 0,
|
||||
loaded_in_global_threads: true,
|
||||
},
|
||||
] as Thread[];
|
||||
|
||||
@@ -154,7 +152,6 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
||||
is_following: true,
|
||||
unread_replies: 2,
|
||||
unread_mentions: 0,
|
||||
loaded_in_global_threads: false,
|
||||
},
|
||||
] as Thread[];
|
||||
|
||||
@@ -163,13 +160,13 @@ describe('*** Operator: Thread Handlers tests ***', () => {
|
||||
team_id_2: team2Threads,
|
||||
};
|
||||
|
||||
await operator.handleThreadInTeam({threadsMap, prepareRecordsOnly: false});
|
||||
await operator.handleThreadInTeam({threadsMap, loadedInGlobalThreads: true, prepareRecordsOnly: false});
|
||||
|
||||
expect(spyOnPrepareRecords).toHaveBeenCalledWith({
|
||||
createRaws: [{
|
||||
raw: {team_id: 'team_id_1', thread_id: 'thread-2', loaded_in_global_threads: true},
|
||||
}, {
|
||||
raw: {team_id: 'team_id_2', thread_id: 'thread-2', loaded_in_global_threads: false},
|
||||
raw: {team_id: 'team_id_2', thread_id: 'thread-2', loaded_in_global_threads: true},
|
||||
}],
|
||||
transformer: transformThreadInTeamRecord,
|
||||
updateRaws: [
|
||||
|
||||
@@ -23,7 +23,7 @@ const {
|
||||
} = Database.MM_TABLES.SERVER;
|
||||
|
||||
export interface ThreadHandlerMix {
|
||||
handleThreads: ({threads, teamId, prepareRecordsOnly}: HandleThreadsArgs) => Promise<Model[]>;
|
||||
handleThreads: ({threads, teamId, prepareRecordsOnly, loadedInGlobalThreads}: HandleThreadsArgs) => Promise<Model[]>;
|
||||
handleThreadParticipants: ({threadsParticipants, prepareRecordsOnly}: HandleThreadParticipantsArgs) => Promise<ThreadParticipantModel[]>;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
||||
* @param {boolean | undefined} handleThreads.prepareRecordsOnly
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
handleThreads = async ({threads, teamId, prepareRecordsOnly = false}: HandleThreadsArgs): Promise<Model[]> => {
|
||||
handleThreads = async ({threads, teamId, loadedInGlobalThreads, prepareRecordsOnly = false}: HandleThreadsArgs): Promise<Model[]> => {
|
||||
if (!threads.length) {
|
||||
throw new DataOperatorException(
|
||||
'An empty "threads" array has been passed to the handleThreads method',
|
||||
@@ -81,6 +81,7 @@ const ThreadHandler = (superclass: any) => class extends superclass {
|
||||
const threadsInTeam = await this.handleThreadInTeam({
|
||||
threadsMap: {[teamId]: threads},
|
||||
prepareRecordsOnly: true,
|
||||
loadedInGlobalThreads,
|
||||
}) as ThreadInTeamModel[];
|
||||
batch.push(...threadsInTeam);
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ import type {HandleThreadInTeamArgs, RecordPair} from '@typings/database/databas
|
||||
import type ThreadInTeamModel from '@typings/database/models/servers/thread_in_team';
|
||||
|
||||
export interface ThreadInTeamHandlerMix {
|
||||
handleThreadInTeam: ({threadsMap, prepareRecordsOnly}: HandleThreadInTeamArgs) => Promise<ThreadInTeamModel[]>;
|
||||
handleThreadInTeam: ({threadsMap, loadedInGlobalThreads, 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[]> => {
|
||||
handleThreadInTeam = async ({threadsMap, loadedInGlobalThreads, prepareRecordsOnly = false}: HandleThreadInTeamArgs): Promise<ThreadInTeamModel[]> => {
|
||||
if (!Object.keys(threadsMap).length) {
|
||||
return [];
|
||||
}
|
||||
@@ -40,11 +40,11 @@ const ThreadInTeamHandler = (superclass: any) => class extends superclass {
|
||||
const newValue = {
|
||||
thread_id: thread.id,
|
||||
team_id: teamId,
|
||||
loaded_in_global_threads: thread.loaded_in_global_threads,
|
||||
loaded_in_global_threads: Boolean(loadedInGlobalThreads),
|
||||
};
|
||||
|
||||
// update record if loaded_in_global_threads is true
|
||||
if (chunk && thread.loaded_in_global_threads) {
|
||||
// update record only if loaded_in_global_threads is true
|
||||
if (chunk && loadedInGlobalThreads) {
|
||||
update.push(getValidRecordsForUpdate({
|
||||
tableName: THREADS_IN_TEAM,
|
||||
newValue,
|
||||
|
||||
@@ -35,8 +35,8 @@ export const transformThreadRecord = ({action, database, value}: TransformerArgs
|
||||
thread.lastViewedAt = raw.last_viewed_at ?? record?.lastViewedAt ?? 0;
|
||||
thread.replyCount = raw.reply_count;
|
||||
thread.isFollowing = raw.is_following ?? record?.isFollowing;
|
||||
thread.unreadReplies = raw.unread_replies ?? record?.lastViewedAt ?? 0;
|
||||
thread.unreadMentions = raw.unread_mentions ?? record?.lastViewedAt ?? 0;
|
||||
thread.unreadReplies = raw.unread_replies ?? record?.unreadReplies ?? 0;
|
||||
thread.unreadMentions = raw.unread_mentions ?? record?.unreadMentions ?? 0;
|
||||
thread.viewedAt = record?.viewedAt || 0;
|
||||
};
|
||||
|
||||
|
||||
1
types/api/threads.d.ts
vendored
1
types/api/threads.d.ts
vendored
@@ -11,7 +11,6 @@ type Thread = {
|
||||
is_following?: boolean;
|
||||
unread_replies: number;
|
||||
unread_mentions: number;
|
||||
loaded_in_global_threads: boolean;
|
||||
};
|
||||
|
||||
type ThreadParticipant = {
|
||||
|
||||
2
types/database/database.d.ts
vendored
2
types/database/database.d.ts
vendored
@@ -92,6 +92,7 @@ export type HandleThreadsArgs = {
|
||||
threads: Thread[];
|
||||
prepareRecordsOnly?: boolean;
|
||||
teamId?: string;
|
||||
loadedInGlobalThreads?: boolean;
|
||||
};
|
||||
|
||||
export type HandleThreadParticipantsArgs = {
|
||||
@@ -103,6 +104,7 @@ export type HandleThreadParticipantsArgs = {
|
||||
export type HandleThreadInTeamArgs = {
|
||||
threadsMap: Record<string, Thread[]>;
|
||||
prepareRecordsOnly?: boolean;
|
||||
loadedInGlobalThreads?: boolean;
|
||||
};
|
||||
|
||||
export type SanitizeReactionsArgs = {
|
||||
|
||||
Reference in New Issue
Block a user