forked from Ivasoft/mattermost-mobile
[Gekidou] Fetch (and save) groups on @mention auto-complete (#6323)
* WIP * Actions updated to fetch remote first, and local on error * Groups fetch and save * PR Feedback: prepare vs store and undefined fix * Forgot to add file
This commit is contained in:
95
app/actions/local/group.ts
Normal file
95
app/actions/local/group.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {fetchFilteredChannelGroups, fetchFilteredTeamGroups, fetchGroupsForAutocomplete} from '@actions/remote/groups';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {prepareGroups, queryGroupsByName, queryGroupsByNameInChannel, queryGroupsByNameInTeam} from '@queries/servers/group';
|
||||
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
|
||||
export const searchGroupsByName = async (serverUrl: string, name: string): Promise<GroupModel[]> => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
throw new Error(`${serverUrl} operator not found`);
|
||||
}
|
||||
|
||||
try {
|
||||
const groups = await fetchGroupsForAutocomplete(serverUrl, name);
|
||||
|
||||
if (groups && Array.isArray(groups)) {
|
||||
return groups;
|
||||
}
|
||||
throw groups.error;
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('searchGroupsByName - ERROR', e);
|
||||
return queryGroupsByName(operator.database, name).fetch();
|
||||
}
|
||||
};
|
||||
|
||||
export const searchGroupsByNameInTeam = async (serverUrl: string, name: string, teamId: string): Promise<GroupModel[]> => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
throw new Error(`${serverUrl} operator not found`);
|
||||
}
|
||||
|
||||
try {
|
||||
const groups = await fetchFilteredTeamGroups(serverUrl, name, teamId);
|
||||
|
||||
if (groups && Array.isArray(groups)) {
|
||||
return groups;
|
||||
}
|
||||
throw groups.error;
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('searchGroupsByNameInTeam - ERROR', e);
|
||||
return queryGroupsByNameInTeam(operator.database, name, teamId).fetch();
|
||||
}
|
||||
};
|
||||
|
||||
export const searchGroupsByNameInChannel = async (serverUrl: string, name: string, channelId: string): Promise<GroupModel[]> => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
throw new Error(`${serverUrl} operator not found`);
|
||||
}
|
||||
|
||||
try {
|
||||
const groups = await fetchFilteredChannelGroups(serverUrl, name, channelId);
|
||||
|
||||
if (groups && Array.isArray(groups)) {
|
||||
return groups;
|
||||
}
|
||||
throw groups.error;
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('searchGroupsByNameInChannel - ERROR', e);
|
||||
return queryGroupsByNameInChannel(operator.database, name, channelId).fetch();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Store fetched groups locally
|
||||
*
|
||||
* @param serverUrl string - The Server URL
|
||||
* @param groups Group[] - The groups fetched from the API
|
||||
* @param prepareRecordsOnly boolean - Wether to only prepare records without saving
|
||||
*/
|
||||
export const storeGroups = async (serverUrl: string, groups: Group[]) => {
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
throw new Error(`${serverUrl} operator not found`);
|
||||
}
|
||||
|
||||
try {
|
||||
const preparedGroups = await prepareGroups(operator, groups);
|
||||
|
||||
if (preparedGroups.length) {
|
||||
operator.batchRecords(preparedGroups);
|
||||
}
|
||||
|
||||
return preparedGroups;
|
||||
} catch (e) {
|
||||
return {error: e};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {storeGroups} from '@actions/local/group';
|
||||
import {prepareGroups} from '@app/queries/servers/group';
|
||||
import {Client} from '@client/rest';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import NetworkManager from '@managers/network_manager';
|
||||
|
||||
import {forceLogoutIfNecessary} from './session';
|
||||
|
||||
export const fetchGroupsForChannel = async (serverUrl: string, channelId: string) => {
|
||||
let client: Client;
|
||||
export const fetchGroupsForAutocomplete = async (serverUrl: string, query: string, fetchOnly = false) => {
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return client.getAllGroupsAssociatedToChannel(channelId);
|
||||
const client: Client = NetworkManager.getClient(serverUrl);
|
||||
const response = await client.getGroups(query);
|
||||
|
||||
// Save locally
|
||||
if (!fetchOnly) {
|
||||
return await storeGroups(serverUrl, response);
|
||||
}
|
||||
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
throw new Error(`${serverUrl} operator not found`);
|
||||
}
|
||||
|
||||
return await prepareGroups(operator, response);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchGroupsForMembership = async (serverUrl: string, userId: string) => {
|
||||
let client: Client;
|
||||
export const fetchGroupsForChannel = async (serverUrl: string, channelId: string, fetchOnly = false) => {
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return client.getAllGroupsAssociatedToMembership(userId);
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
const response = await client.getAllGroupsAssociatedToChannel(channelId);
|
||||
|
||||
if (!fetchOnly) {
|
||||
return await storeGroups(serverUrl, response.groups);
|
||||
}
|
||||
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
throw new Error(`${serverUrl} operator not found`);
|
||||
}
|
||||
|
||||
return await prepareGroups(operator, response.groups);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchGroupsForTeam = async (serverUrl: string, teamId: string) => {
|
||||
let client: Client;
|
||||
export const fetchGroupsForTeam = async (serverUrl: string, teamId: string, fetchOnly = false) => {
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return client.getAllGroupsAssociatedToTeam(teamId);
|
||||
const client: Client = NetworkManager.getClient(serverUrl);
|
||||
const response = await client.getAllGroupsAssociatedToTeam(teamId);
|
||||
|
||||
if (!fetchOnly) {
|
||||
return await storeGroups(serverUrl, response.groups);
|
||||
}
|
||||
|
||||
// return await storeGroups(serverUrl, response.groups, true);
|
||||
const operator = DatabaseManager.serverDatabases[serverUrl]?.operator;
|
||||
if (!operator) {
|
||||
throw new Error(`${serverUrl} operator not found`);
|
||||
}
|
||||
|
||||
return await prepareGroups(operator, response.groups);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchGroupsForAutocomplete = async (serverUrl: string, query: string) => {
|
||||
let client: Client;
|
||||
export const fetchFilteredTeamGroups = async (serverUrl: string, searchTerm: string, teamId: string) => {
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return client.getGroups(query);
|
||||
const groups = await fetchGroupsForTeam(serverUrl, teamId);
|
||||
|
||||
if (groups && Array.isArray(groups)) {
|
||||
return groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
}
|
||||
|
||||
throw groups.error;
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchMembershipsForGroup = async (serverUrl: string, groupId: string) => {
|
||||
let client: Client;
|
||||
export const fetchFilteredChannelGroups = async (serverUrl: string, searchTerm: string, channelId: string) => {
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return client.getAllMembershipsAssociatedToGroup(groupId);
|
||||
const groups = await fetchGroupsForChannel(serverUrl, channelId);
|
||||
|
||||
if (groups && Array.isArray(groups)) {
|
||||
return groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
}
|
||||
|
||||
throw groups.error;
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchTeamsForGroup = async (serverUrl: string, groupId: string) => {
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return client.getAllTeamsAssociatedToGroup(groupId);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchChannelsForGroup = async (serverUrl: string, groupId: string) => {
|
||||
let client: Client;
|
||||
try {
|
||||
client = NetworkManager.getClient(serverUrl);
|
||||
return client.getAllChannelsAssociatedToGroup(groupId);
|
||||
} catch (error) {
|
||||
forceLogoutIfNecessary(serverUrl, error as ClientErrorProps);
|
||||
return {error};
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchFilteredTeamGroups = async (serverUrl: string, teamId: string, searchTerm: string) => {
|
||||
const response = await fetchGroupsForTeam(serverUrl, teamId);
|
||||
|
||||
if (response && 'groups' in response) {
|
||||
return response.groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
export const fetchFilteredChannelGroups = async (serverUrl: string, channelId: string, searchTerm: string) => {
|
||||
const response = await fetchGroupsForChannel(serverUrl, channelId);
|
||||
|
||||
if (response && 'groups' in response) {
|
||||
return response.groups.filter((g) => g.name.toLowerCase().includes(searchTerm.toLowerCase()));
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import {debounce} from 'lodash';
|
||||
import React, {useCallback, useEffect, useMemo, useState} from 'react';
|
||||
import {Platform, SectionList, SectionListData, SectionListRenderItemInfo} from 'react-native';
|
||||
|
||||
import {fetchFilteredChannelGroups, fetchFilteredTeamGroups, fetchGroupsForAutocomplete} from '@actions/remote/groups';
|
||||
import {searchGroupsByName, searchGroupsByNameInChannel, searchGroupsByNameInTeam} from '@actions/local/group';
|
||||
import {searchUsers} from '@actions/remote/user';
|
||||
import GroupMentionItem from '@components/autocomplete/at_mention_group/at_mention_group';
|
||||
import AtMentionItem from '@components/autocomplete/at_mention_item';
|
||||
@@ -19,6 +19,7 @@ import {t} from '@i18n';
|
||||
import {queryAllUsers} from '@queries/servers/user';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
const SECTION_KEY_TEAM_MEMBERS = 'teamMembers';
|
||||
@@ -33,7 +34,7 @@ type SpecialMention = {
|
||||
defaultMessage: string;
|
||||
}
|
||||
|
||||
type UserMentionSections = Array<SectionListData<UserProfile|UserModel|Group|SpecialMention>>
|
||||
type UserMentionSections = Array<SectionListData<UserProfile|UserModel|GroupModel|SpecialMention>>
|
||||
|
||||
const getMatchTermForAtMention = (() => {
|
||||
let lastMatchTerm: string | null = null;
|
||||
@@ -93,7 +94,7 @@ const filterLocalResults = (users: UserModel[], term: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
const makeSections = (teamMembers: Array<UserProfile | UserModel>, usersInChannel: Array<UserProfile | UserModel>, usersOutOfChannel: Array<UserProfile | UserModel>, groups: Group[], showSpecialMentions: boolean, isLocal = false, isSearch = false) => {
|
||||
const makeSections = (teamMembers: Array<UserProfile | UserModel>, usersInChannel: Array<UserProfile | UserModel>, usersOutOfChannel: Array<UserProfile | UserModel>, groups: GroupModel[], showSpecialMentions: boolean, isLocal = false, isSearch = false) => {
|
||||
const newSections: UserMentionSections = [];
|
||||
|
||||
if (isSearch) {
|
||||
@@ -200,7 +201,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
const emptyProfileList: UserProfile[] = [];
|
||||
const emptyModelList: UserModel[] = [];
|
||||
const emptySectionList: UserMentionSections = [];
|
||||
const emptyGroupList: Group[] = [];
|
||||
const emptyGroupList: GroupModel[] = [];
|
||||
|
||||
const getAllUsers = async (serverUrl: string) => {
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
@@ -233,7 +234,7 @@ const AtMention = ({
|
||||
const [sections, setSections] = useState<UserMentionSections>(emptySectionList);
|
||||
const [usersInChannel, setUsersInChannel] = useState<UserProfile[]>(emptyProfileList);
|
||||
const [usersOutOfChannel, setUsersOutOfChannel] = useState<UserProfile[]>(emptyProfileList);
|
||||
const [groups, setGroups] = useState<Group[]>(emptyGroupList);
|
||||
const [groups, setGroups] = useState<GroupModel[]>(emptyGroupList);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [noResultsTerm, setNoResultsTerm] = useState<string|null>(null);
|
||||
const [localCursorPosition, setLocalCursorPosition] = useState(cursorPosition); // To avoid errors due to delay between value changes and cursor position changes.
|
||||
@@ -311,12 +312,12 @@ const AtMention = ({
|
||||
);
|
||||
}, [completeMention]);
|
||||
|
||||
const renderGroupMentions = useCallback((item: Group) => {
|
||||
const renderGroupMentions = useCallback((item: GroupModel) => {
|
||||
return (
|
||||
<GroupMentionItem
|
||||
key={`autocomplete-group-${item.name}`}
|
||||
name={item.name}
|
||||
displayName={item.display_name}
|
||||
displayName={item.displayName}
|
||||
onPress={completeMention}
|
||||
/>
|
||||
);
|
||||
@@ -332,12 +333,12 @@ const AtMention = ({
|
||||
);
|
||||
}, [completeMention]);
|
||||
|
||||
const renderItem = useCallback(({item, section}: SectionListRenderItemInfo<SpecialMention | Group | UserProfile>) => {
|
||||
const renderItem = useCallback(({item, section}: SectionListRenderItemInfo<SpecialMention | GroupModel | UserProfile>) => {
|
||||
switch (section.key) {
|
||||
case SECTION_KEY_SPECIAL:
|
||||
return renderSpecialMentions(item as SpecialMention);
|
||||
case SECTION_KEY_GROUPS:
|
||||
return renderGroupMentions(item as Group);
|
||||
return renderGroupMentions(item as GroupModel);
|
||||
default:
|
||||
return renderAtMentions(item as UserProfile);
|
||||
}
|
||||
@@ -363,7 +364,7 @@ const AtMention = ({
|
||||
if (useGroupMentions && matchTerm && matchTerm !== '') {
|
||||
// If the channel is constrained, we only show groups for that channel
|
||||
if (isChannelConstrained && channelId) {
|
||||
fetchFilteredChannelGroups(serverUrl, channelId, matchTerm).then((g) => {
|
||||
searchGroupsByNameInChannel(serverUrl, matchTerm, channelId).then((g) => {
|
||||
setGroups(g.length ? g : emptyGroupList);
|
||||
}).catch(() => {
|
||||
setGroups(emptyGroupList);
|
||||
@@ -372,7 +373,7 @@ const AtMention = ({
|
||||
|
||||
// If there is no channel constraint, but a team constraint - only show groups for team
|
||||
if (isTeamConstrained && !isChannelConstrained) {
|
||||
fetchFilteredTeamGroups(serverUrl, teamId!, matchTerm).then((g) => {
|
||||
searchGroupsByNameInTeam(serverUrl, matchTerm, teamId!).then((g) => {
|
||||
setGroups(g.length ? g : emptyGroupList);
|
||||
}).catch(() => {
|
||||
setGroups(emptyGroupList);
|
||||
@@ -381,7 +382,7 @@ const AtMention = ({
|
||||
|
||||
// No constraints? Search all groups
|
||||
if (!isTeamConstrained && !isChannelConstrained) {
|
||||
fetchGroupsForAutocomplete(serverUrl, matchTerm || '').then((g) => {
|
||||
searchGroupsByName(serverUrl, matchTerm || '').then((g) => {
|
||||
setGroups(Array.isArray(g) ? g : emptyGroupList);
|
||||
}).catch(() => {
|
||||
setGroups(emptyGroupList);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {
|
||||
transformGroupRecord,
|
||||
} from '@database/operator/server_data_operator/transformers/group';
|
||||
|
||||
import ServerDataOperator from '..';
|
||||
|
||||
describe('*** Operator: Group Handlers tests ***', () => {
|
||||
let operator: ServerDataOperator;
|
||||
beforeAll(async () => {
|
||||
await DatabaseManager.init(['baseHandler.test.com']);
|
||||
operator = DatabaseManager.serverDatabases['baseHandler.test.com'].operator;
|
||||
});
|
||||
|
||||
it('=> handleGroups: should write to the GROUP table', async () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const spyOnHandleRecords = jest.spyOn(operator, 'handleRecords');
|
||||
const groups: Group[] = [
|
||||
{
|
||||
id: 'kjlw9j1ttnxwig7tnqgebg7dtipno',
|
||||
name: 'test',
|
||||
display_name: 'Test',
|
||||
source: 'custom',
|
||||
remote_id: 'iuh4r89egnslnvakjsdjhg',
|
||||
description: 'Test description',
|
||||
member_count: 0,
|
||||
allow_reference: true,
|
||||
create_at: 0,
|
||||
update_at: 0,
|
||||
delete_at: 0,
|
||||
},
|
||||
];
|
||||
|
||||
await operator.handleGroups({
|
||||
groups,
|
||||
prepareRecordsOnly: false,
|
||||
});
|
||||
|
||||
expect(spyOnHandleRecords).toHaveBeenCalledTimes(1);
|
||||
expect(spyOnHandleRecords).toHaveBeenCalledWith({
|
||||
fieldName: 'id',
|
||||
createOrUpdateRawValues: groups,
|
||||
tableName: MM_TABLES.SERVER.GROUP,
|
||||
prepareRecordsOnly: false,
|
||||
transformer: transformGroupRecord,
|
||||
});
|
||||
});
|
||||
});
|
||||
46
app/database/operator/server_data_operator/handlers/group.ts
Normal file
46
app/database/operator/server_data_operator/handlers/group.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
import {transformGroupRecord} from '@database/operator/server_data_operator/transformers/group';
|
||||
import {getUniqueRawsBy} from '@database/operator/utils/general';
|
||||
|
||||
import type {HandleGroupArgs} from '@typings/database/database';
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
|
||||
const {GROUP} = MM_TABLES.SERVER;
|
||||
export interface GroupHandlerMix {
|
||||
handleGroups: ({groups, prepareRecordsOnly}: HandleGroupArgs) => Promise<GroupModel[]>;
|
||||
}
|
||||
|
||||
const GroupHandler = (superclass: any) => class extends superclass implements GroupHandlerMix {
|
||||
/**
|
||||
* handleGroups: Handler responsible for the Create/Update operations occurring on the Group table from the 'Server' schema
|
||||
* @param {HandleGroupArgs} groupsArgs
|
||||
* @param {Group[]} groupsArgs.groups
|
||||
* @param {boolean} groupsArgs.prepareRecordsOnly
|
||||
* @throws DataOperatorException
|
||||
* @returns {Promise<GroupModel[]>}
|
||||
*/
|
||||
handleGroups = async ({groups, prepareRecordsOnly = true}: HandleGroupArgs): Promise<GroupModel[]> => {
|
||||
if (!groups?.length) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
'An empty or undefined "groups" array has been passed to the handleGroups method',
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const createOrUpdateRawValues = getUniqueRawsBy({raws: groups, key: 'id'});
|
||||
|
||||
return this.handleRecords({
|
||||
fieldName: 'id',
|
||||
transformer: transformGroupRecord,
|
||||
createOrUpdateRawValues,
|
||||
tableName: GROUP,
|
||||
prepareRecordsOnly,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export default GroupHandler;
|
||||
@@ -4,6 +4,7 @@
|
||||
import ServerDataOperatorBase from '@database/operator/server_data_operator/handlers';
|
||||
import CategoryHandler, {CategoryHandlerMix} from '@database/operator/server_data_operator/handlers/category';
|
||||
import ChannelHandler, {ChannelHandlerMix} from '@database/operator/server_data_operator/handlers/channel';
|
||||
import GroupHandler, {GroupHandlerMix} from '@database/operator/server_data_operator/handlers/group';
|
||||
import PostHandler, {PostHandlerMix} from '@database/operator/server_data_operator/handlers/post';
|
||||
import PostsInChannelHandler, {PostsInChannelHandlerMix} from '@database/operator/server_data_operator/handlers/posts_in_channel';
|
||||
import PostsInThreadHandler, {PostsInThreadHandlerMix} from '@database/operator/server_data_operator/handlers/posts_in_thread';
|
||||
@@ -16,12 +17,25 @@ import mix from '@utils/mix';
|
||||
|
||||
import type {Database} from '@nozbe/watermelondb';
|
||||
|
||||
interface ServerDataOperator extends ServerDataOperatorBase, PostHandlerMix, PostsInChannelHandlerMix,
|
||||
PostsInThreadHandlerMix, ReactionHandlerMix, UserHandlerMix, ChannelHandlerMix, CategoryHandlerMix, TeamHandlerMix, ThreadHandlerMix, ThreadInTeamHandlerMix {}
|
||||
interface ServerDataOperator extends
|
||||
CategoryHandlerMix,
|
||||
ChannelHandlerMix,
|
||||
GroupHandlerMix,
|
||||
PostHandlerMix,
|
||||
PostsInChannelHandlerMix,
|
||||
PostsInThreadHandlerMix,
|
||||
ReactionHandlerMix,
|
||||
ServerDataOperatorBase,
|
||||
TeamHandlerMix,
|
||||
ThreadHandlerMix,
|
||||
ThreadInTeamHandlerMix,
|
||||
UserHandlerMix
|
||||
{}
|
||||
|
||||
class ServerDataOperator extends mix(ServerDataOperatorBase).with(
|
||||
CategoryHandler,
|
||||
ChannelHandler,
|
||||
GroupHandler,
|
||||
PostHandler,
|
||||
PostsInChannelHandler,
|
||||
PostsInThreadHandler,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {
|
||||
transformGroupRecord,
|
||||
} from '@database/operator/server_data_operator/transformers/group';
|
||||
import {createTestConnection} from '@database/operator/utils/create_test_connection';
|
||||
import {OperationType} from '@typings/database/enums';
|
||||
|
||||
describe('*** GROUP Prepare Records Test ***', () => {
|
||||
it('=> transformGroupRecord: should return an array of type GroupModel', async () => {
|
||||
// expect.assertions(3);
|
||||
|
||||
const database = await createTestConnection({databaseName: 'group_prepare_records', setActive: true});
|
||||
expect(database).toBeTruthy();
|
||||
|
||||
const preparedRecords = await transformGroupRecord({
|
||||
action: OperationType.CREATE,
|
||||
database: database!,
|
||||
value: {
|
||||
record: undefined,
|
||||
raw: {
|
||||
id: 'kow9j1ttnxwig7tnqgebg7dtipno',
|
||||
display_name: 'Test',
|
||||
name: 'recent',
|
||||
source: 'custom',
|
||||
remote_id: 'custom',
|
||||
} as Group,
|
||||
},
|
||||
});
|
||||
|
||||
expect(preparedRecords).toBeTruthy();
|
||||
expect(preparedRecords.collection.modelClass.name).toBe('GroupModel');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
// 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 GroupModel from '@typings/database/models/servers/group';
|
||||
|
||||
const {
|
||||
GROUP,
|
||||
} = MM_TABLES.SERVER;
|
||||
|
||||
/**
|
||||
* transformGroupRecord: Prepares a record of the SERVER database 'Group' table for update or create actions.
|
||||
* @param {TransformerArgs} operator
|
||||
* @param {Database} operator.database
|
||||
* @param {RecordPair} operator.value
|
||||
* @returns {Promise<GroupModel>}
|
||||
*/
|
||||
export const transformGroupRecord = ({action, database, value}: TransformerArgs): Promise<GroupModel> => {
|
||||
const raw = value.raw as Group;
|
||||
const record = value.record as GroupModel;
|
||||
const isCreateAction = action === OperationType.CREATE;
|
||||
|
||||
// id of group comes from server response
|
||||
const fieldsMapper = (group: GroupModel) => {
|
||||
group._raw.id = isCreateAction ? (raw?.id ?? group.id) : record.id;
|
||||
group.name = raw.name;
|
||||
group.displayName = raw.display_name;
|
||||
group.source = raw.source;
|
||||
group.remoteId = raw.remote_id;
|
||||
};
|
||||
|
||||
return prepareBaseRecord({
|
||||
action,
|
||||
database,
|
||||
tableName: GROUP,
|
||||
value,
|
||||
fieldsMapper,
|
||||
}) as Promise<GroupModel>;
|
||||
};
|
||||
35
app/queries/servers/group.ts
Normal file
35
app/queries/servers/group.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Database, Q} from '@nozbe/watermelondb';
|
||||
|
||||
import {MM_TABLES} from '@constants/database';
|
||||
|
||||
import type ServerDataOperator from '@database/operator/server_data_operator';
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
|
||||
const {SERVER: {GROUP, GROUP_CHANNEL, GROUP_TEAM}} = MM_TABLES;
|
||||
|
||||
export const queryGroupsByName = (database: Database, name: string) => {
|
||||
return database.collections.get<GroupModel>(GROUP).query(
|
||||
Q.where('name', Q.like(`%${Q.sanitizeLikeString(name)}%`)),
|
||||
);
|
||||
};
|
||||
|
||||
export const queryGroupsByNameInTeam = (database: Database, name: string, teamId: string) => {
|
||||
return database.collections.get<GroupModel>(GROUP).query(
|
||||
Q.on(GROUP_TEAM, 'team_id', teamId),
|
||||
Q.where('name', Q.like(`%${Q.sanitizeLikeString(name)}%`)),
|
||||
);
|
||||
};
|
||||
|
||||
export const queryGroupsByNameInChannel = (database: Database, name: string, channelId: string) => {
|
||||
return database.collections.get<GroupModel>(GROUP).query(
|
||||
Q.on(GROUP_CHANNEL, 'channel_id', channelId),
|
||||
Q.where('name', Q.like(`%${Q.sanitizeLikeString(name)}%`)),
|
||||
);
|
||||
};
|
||||
|
||||
export const prepareGroups = (operator: ServerDataOperator, groups: Group[]) => {
|
||||
return operator.handleGroups({groups, prepareRecordsOnly: true});
|
||||
};
|
||||
4
types/database/database.d.ts
vendored
4
types/database/database.d.ts
vendored
@@ -218,6 +218,10 @@ export type HandleCategoryArgs = PrepareOnly & {
|
||||
categories?: Category[];
|
||||
};
|
||||
|
||||
export type HandleGroupArgs = PrepareOnly & {
|
||||
groups?: Group[];
|
||||
};
|
||||
|
||||
export type HandleCategoryChannelArgs = PrepareOnly & {
|
||||
categoryChannels?: CategoryChannel[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user