From 7f8a55684dc02e228859823fa854a7c35c8dcc68 Mon Sep 17 00:00:00 2001 From: Avinash Lingaloo Date: Tue, 29 Mar 2022 21:54:09 +0400 Subject: [PATCH] MM-42787 - Gekidou User DB field remote_id (#6094) * added remote_id to user table schema * update user model to accomodate for remote_id field * transform - reaction - split into its own file * update user transformer * update isShared function * Fix typescript * make remote_id field optional Co-authored-by: Elias Nahum --- app/database/models/server/user.ts | 8 +++- .../server_data_operator/handlers/reaction.ts | 2 +- .../transformers/reaction.test.ts | 35 ++++++++++++++++ .../transformers/reaction.ts | 41 +++++++++++++++++++ .../transformers/user.test.ts | 33 +-------------- .../server_data_operator/transformers/user.ts | 38 +---------------- .../schema/server/table_schemas/user.ts | 1 + app/database/schema/server/test.ts | 2 + app/utils/user/index.ts | 6 +-- types/database/models/servers/user.d.ts | 3 ++ 10 files changed, 95 insertions(+), 74 deletions(-) create mode 100644 app/database/operator/server_data_operator/transformers/reaction.test.ts create mode 100644 app/database/operator/server_data_operator/transformers/reaction.ts diff --git a/app/database/models/server/user.ts b/app/database/models/server/user.ts index bc85231840..924008b31b 100644 --- a/app/database/models/server/user.ts +++ b/app/database/models/server/user.ts @@ -14,6 +14,7 @@ import type PostModel from '@typings/database/models/servers/post'; import type PreferenceModel from '@typings/database/models/servers/preference'; import type ReactionModel from '@typings/database/models/servers/reaction'; import type TeamMembershipModel from '@typings/database/models/servers/team_membership'; +import type UserModelInterface from '@typings/database/models/servers/user'; import type {UserMentionKey} from '@typings/global/markdown'; const { @@ -31,7 +32,7 @@ const { * The User model represents the 'USER' table and its relationship to other * shareholders in the app. */ -export default class UserModel extends Model { +export default class UserModel extends Model implements UserModelInterface { /** table (name) : User */ static table = USER; @@ -56,7 +57,7 @@ export default class UserModel extends Model { /** USER has a 1:N relationship with TEAM_MEMBERSHIP. A user can join multiple teams */ [TEAM_MEMBERSHIP]: {type: 'has_many', foreignKey: 'user_id'}, - /** USER has a 1:N relationship with THREAD_PARTICIPANT. A user can participante in multiple threads */ + /** USER has a 1:N relationship with THREAD_PARTICIPANT. A user can participate in multiple threads */ [THREAD_PARTICIPANT]: {type: 'has_many', foreignKey: 'user_id'}, }; @@ -105,6 +106,9 @@ export default class UserModel extends Model { /** username : The user's username */ @field('username') username!: string; + /** remote_id : The ID of the remote organization that this user belongs to */ + @field('remote_id') remoteId!: string | null; + /** notify_props : Notification preferences/configurations */ @json('notify_props', safeParseJSON) notifyProps!: UserNotifyProps | null; diff --git a/app/database/operator/server_data_operator/handlers/reaction.ts b/app/database/operator/server_data_operator/handlers/reaction.ts index 08c3c2e282..c9eabc2687 100644 --- a/app/database/operator/server_data_operator/handlers/reaction.ts +++ b/app/database/operator/server_data_operator/handlers/reaction.ts @@ -3,7 +3,7 @@ import {MM_TABLES} from '@constants/database'; import DataOperatorException from '@database/exceptions/data_operator_exception'; -import {transformReactionRecord} from '@database/operator/server_data_operator/transformers/user'; +import {transformReactionRecord} from '@database/operator/server_data_operator/transformers/reaction'; import {sanitizeReactions} from '@database/operator/utils/reaction'; import type {HandleReactionsArgs} from '@typings/database/database'; diff --git a/app/database/operator/server_data_operator/transformers/reaction.test.ts b/app/database/operator/server_data_operator/transformers/reaction.test.ts new file mode 100644 index 0000000000..fc404345bc --- /dev/null +++ b/app/database/operator/server_data_operator/transformers/reaction.test.ts @@ -0,0 +1,35 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {transformReactionRecord} from '@database/operator/server_data_operator/transformers/reaction'; +import {createTestConnection} from '@database/operator/utils/create_test_connection'; +import {OperationType} from '@typings/database/enums'; + +describe('*** REACTION Prepare Records Test ***', () => { + it('=> transformReactionRecord: should return an array of type Reaction', async () => { + expect.assertions(3); + + const database = await createTestConnection({databaseName: 'reaction_prepare_records', setActive: true}); + expect(database).toBeTruthy(); + + const preparedRecords = await transformReactionRecord({ + action: OperationType.CREATE, + database: database!, + value: { + record: undefined, + raw: { + id: 'ps81iqbddesfby8jayz7owg4yypoo', + user_id: 'q3mzxua9zjfczqakxdkowc6u6yy', + post_id: 'ps81iqbddesfby8jayz7owg4yypoo', + emoji_name: 'thumbsup', + create_at: 1596032651748, + update_at: 1608253011321, + delete_at: 0, + }, + }, + }); + + expect(preparedRecords).toBeTruthy(); + expect(preparedRecords!.collection.modelClass.name).toBe('ReactionModel'); + }); +}); diff --git a/app/database/operator/server_data_operator/transformers/reaction.ts b/app/database/operator/server_data_operator/transformers/reaction.ts new file mode 100644 index 0000000000..78b762c237 --- /dev/null +++ b/app/database/operator/server_data_operator/transformers/reaction.ts @@ -0,0 +1,41 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {MM_TABLES} from '@constants/database'; +import {prepareBaseRecord} from '@database/operator/server_data_operator/transformers/index'; +import {OperationType} from '@typings/database/enums'; + +import type {TransformerArgs} from '@typings/database/database'; +import type ReactionModel from '@typings/database/models/servers/reaction'; + +const {REACTION} = MM_TABLES.SERVER; + +/** + * transformReactionRecord: Prepares a record of the SERVER database 'Reaction' table for update or create actions. + * @param {TransformerArgs} operator + * @param {Database} operator.database + * @param {RecordPair} operator.value + * @returns {Promise} + */ +export const transformReactionRecord = ({action, database, value}: TransformerArgs): Promise => { + const raw = value.raw as Reaction; + const record = value.record as ReactionModel; + const isCreateAction = action === OperationType.CREATE; + + // id of reaction comes from server response + const fieldsMapper = (reaction: ReactionModel) => { + reaction._raw.id = isCreateAction ? (raw?.id ?? reaction.id) : record.id; + reaction.userId = raw.user_id; + reaction.postId = raw.post_id; + reaction.emojiName = raw.emoji_name; + reaction.createAt = raw.create_at; + }; + + return prepareBaseRecord({ + action, + database, + tableName: REACTION, + value, + fieldsMapper, + }) as Promise; +}; diff --git a/app/database/operator/server_data_operator/transformers/user.test.ts b/app/database/operator/server_data_operator/transformers/user.test.ts index 0e214a2328..4d2d3c30bd 100644 --- a/app/database/operator/server_data_operator/transformers/user.test.ts +++ b/app/database/operator/server_data_operator/transformers/user.test.ts @@ -1,11 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import { - transformPreferenceRecord, - transformReactionRecord, - transformUserRecord, -} from '@database/operator/server_data_operator/transformers/user'; +import {transformPreferenceRecord, transformUserRecord} from '@database/operator/server_data_operator/transformers/user'; import {createTestConnection} from '@database/operator/utils/create_test_connection'; import {OperationType} from '@typings/database/enums'; @@ -29,33 +25,6 @@ describe('*** USER Prepare Records Test ***', () => { expect(preparedRecords!.collection.modelClass.name).toBe('PreferenceModel'); }); - it('=> transformReactionRecord: should return an array of type Reaction', async () => { - expect.assertions(3); - - const database = await createTestConnection({databaseName: 'user_prepare_records', setActive: true}); - expect(database).toBeTruthy(); - - const preparedRecords = await transformReactionRecord({ - action: OperationType.CREATE, - database: database!, - value: { - record: undefined, - raw: { - id: 'ps81iqbddesfby8jayz7owg4yypoo', - user_id: 'q3mzxua9zjfczqakxdkowc6u6yy', - post_id: 'ps81iqbddesfby8jayz7owg4yypoo', - emoji_name: 'thumbsup', - create_at: 1596032651748, - update_at: 1608253011321, - delete_at: 0, - }, - }, - }); - - expect(preparedRecords).toBeTruthy(); - expect(preparedRecords!.collection.modelClass.name).toBe('ReactionModel'); - }); - it('=> transformUserRecord: should return an array of type User', async () => { expect.assertions(3); diff --git a/app/database/operator/server_data_operator/transformers/user.ts b/app/database/operator/server_data_operator/transformers/user.ts index f6774aa3a2..1d09ed20d8 100644 --- a/app/database/operator/server_data_operator/transformers/user.ts +++ b/app/database/operator/server_data_operator/transformers/user.ts @@ -7,44 +7,9 @@ import {OperationType} from '@typings/database/enums'; import type {TransformerArgs} from '@typings/database/database'; import type PreferenceModel from '@typings/database/models/servers/preference'; -import type ReactionModel from '@typings/database/models/servers/reaction'; import type UserModel from '@typings/database/models/servers/user'; -const { - PREFERENCE, - REACTION, - USER, -} = MM_TABLES.SERVER; - -/** - * transformReactionRecord: Prepares a record of the SERVER database 'Reaction' table for update or create actions. - * @param {TransformerArgs} operator - * @param {Database} operator.database - * @param {RecordPair} operator.value - * @returns {Promise} - */ -export const transformReactionRecord = ({action, database, value}: TransformerArgs): Promise => { - const raw = value.raw as Reaction; - const record = value.record as ReactionModel; - const isCreateAction = action === OperationType.CREATE; - - // id of reaction comes from server response - const fieldsMapper = (reaction: ReactionModel) => { - reaction._raw.id = isCreateAction ? (raw?.id ?? reaction.id) : record.id; - reaction.userId = raw.user_id; - reaction.postId = raw.post_id; - reaction.emojiName = raw.emoji_name; - reaction.createAt = raw.create_at; - }; - - return prepareBaseRecord({ - action, - database, - tableName: REACTION, - value, - fieldsMapper, - }) as Promise; -}; +const {PREFERENCE, USER} = MM_TABLES.SERVER; /** * transformUserRecord: Prepares a record of the SERVER database 'User' table for update or create actions. @@ -78,6 +43,7 @@ export const transformUserRecord = ({action, database, value}: TransformerArgs): user.props = raw.props || null; user.timezone = raw.timezone || null; user.isBot = raw.is_bot; + user.remoteId = raw?.remote_id ?? ''; if (raw.status) { user.status = raw.status; } diff --git a/app/database/schema/server/table_schemas/user.ts b/app/database/schema/server/table_schemas/user.ts index e5cced5865..cac58562f7 100644 --- a/app/database/schema/server/table_schemas/user.ts +++ b/app/database/schema/server/table_schemas/user.ts @@ -28,5 +28,6 @@ export default tableSchema({ {name: 'status', type: 'string'}, {name: 'timezone', type: 'string'}, {name: 'username', type: 'string'}, + {name: 'remote_id', type: 'string', isOptional: true}, ], }); diff --git a/app/database/schema/server/test.ts b/app/database/schema/server/test.ts index a1b6c12c74..673bff2e40 100644 --- a/app/database/schema/server/test.ts +++ b/app/database/schema/server/test.ts @@ -533,6 +533,7 @@ describe('*** Test schema for SERVER database ***', () => { status: {name: 'status', type: 'string'}, timezone: {name: 'timezone', type: 'string'}, username: {name: 'username', type: 'string'}, + remote_id: {name: 'remote_id', type: 'string', isOptional: true}, }, columnArray: [ {name: 'auth_service', type: 'string'}, @@ -553,6 +554,7 @@ describe('*** Test schema for SERVER database ***', () => { {name: 'status', type: 'string'}, {name: 'timezone', type: 'string'}, {name: 'username', type: 'string'}, + {name: 'remote_id', type: 'string', isOptional: true}, ], }, }, diff --git a/app/utils/user/index.ts b/app/utils/user/index.ts index e0e60b02e7..e6ca5d4fe5 100644 --- a/app/utils/user/index.ts +++ b/app/utils/user/index.ts @@ -6,10 +6,10 @@ import {Alert} from 'react-native'; import {General, Permissions, Preferences} from '@constants'; import {CustomStatusDuration} from '@constants/custom_status'; -import {UserModel} from '@database/models/server'; import {DEFAULT_LOCALE, getLocalizedMessage, t} from '@i18n'; import {toTitleCase} from '@utils/helpers'; +import type UserModel from '@typings/database/models/servers/user'; import type {IntlShape} from 'react-intl'; export function displayUsername(user?: UserProfile | UserModel, locale?: string, teammateDisplayNameSetting?: string, useFallbackUsername = true) { @@ -51,7 +51,7 @@ export function getFullName(user: UserProfile | UserModel): string { let firstName: string; let lastName: string; - if (user instanceof UserModel) { + if ('lastName' in user) { firstName = user.firstName; lastName = user.lastName; } else { @@ -197,7 +197,7 @@ export function isBot(user: UserProfile | UserModel): boolean { } export function isShared(user: UserProfile | UserModel): boolean { - return 'remote_id' in user ? Boolean(user.remote_id) : Boolean(user.props?.remote_id); + return ('remoteId' in user) ? Boolean(user.remoteId) : Boolean(user.remote_id); } export function removeUserFromList(userId: string, originalList: UserProfile[]): UserProfile[] { diff --git a/types/database/models/servers/user.d.ts b/types/database/models/servers/user.d.ts index fe7bb8a2fe..ca7ae44baa 100644 --- a/types/database/models/servers/user.d.ts +++ b/types/database/models/servers/user.d.ts @@ -68,6 +68,9 @@ export default class UserModel extends Model { /** username : The user's username */ username: string; + /** remote_id : The ID of the remote organization that this user belongs to */ + remoteId: string | null; + /** notify_props : Notification preferences/configurations */ notifyProps: UserNotifyProps | null;