[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:
Kyriakos Z
2022-04-06 17:37:17 +03:00
committed by GitHub
parent c85914f4ba
commit 3326f34933
11 changed files with 32 additions and 24 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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),
);
}

View File

@@ -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. */

View File

@@ -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 */

View File

@@ -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: [

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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;
};

View File

@@ -11,7 +11,6 @@ type Thread = {
is_following?: boolean;
unread_replies: number;
unread_mentions: number;
loaded_in_global_threads: boolean;
};
type ThreadParticipant = {

View File

@@ -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 = {