From a3eeb69277c95a5743a208bffb64df0bdafd7f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Mon, 16 Oct 2023 10:13:16 +0200 Subject: [PATCH] Goodbye, GraphQL (#7597) --- app/actions/remote/entry/common.ts | 44 ++- app/actions/remote/entry/gql_common.ts | 310 ------------------- app/actions/websocket/index.ts | 3 +- app/client/graphQL/constants.ts | 16 - app/client/graphQL/entry.ts | 392 ------------------------- app/constants/graphql.ts | 4 - app/queries/servers/channel.ts | 6 +- app/queries/servers/entry.ts | 8 +- app/utils/graphql.ts | 215 -------------- types/api/config.d.ts | 1 - types/api/graphql.d.ts | 186 ------------ 11 files changed, 41 insertions(+), 1144 deletions(-) delete mode 100644 app/actions/remote/entry/gql_common.ts delete mode 100644 app/client/graphQL/constants.ts delete mode 100644 app/client/graphQL/entry.ts delete mode 100644 app/constants/graphql.ts delete mode 100644 app/utils/graphql.ts delete mode 100644 types/api/graphql.d.ts diff --git a/app/actions/remote/entry/common.ts b/app/actions/remote/entry/common.ts index dff04e578..34cd697b1 100644 --- a/app/actions/remote/entry/common.ts +++ b/app/actions/remote/entry/common.ts @@ -6,10 +6,10 @@ import {fetchGroupsForMember} from '@actions/remote/groups'; import {fetchPostsForUnreadChannels} from '@actions/remote/post'; import {type MyPreferencesRequest, fetchMyPreferences} from '@actions/remote/preference'; import {fetchRoles} from '@actions/remote/role'; -import {fetchConfigAndLicense} from '@actions/remote/systems'; +import {fetchConfigAndLicense, fetchDataRetentionPolicy} from '@actions/remote/systems'; import {fetchMyTeams, fetchTeamsChannelsAndUnreadPosts, handleKickFromTeam, type MyTeamsRequest, updateCanJoinTeams} from '@actions/remote/team'; import {syncTeamThreads} from '@actions/remote/thread'; -import {fetchMe, type MyUserRequest, updateAllUsersSince} from '@actions/remote/user'; +import {fetchMe, type MyUserRequest, updateAllUsersSince, autoUpdateTimezone} from '@actions/remote/user'; import {General, Preferences, Screens} from '@constants'; import {SYSTEM_IDENTIFIERS} from '@constants/database'; import {PUSH_PROXY_RESPONSE_NOT_AVAILABLE, PUSH_PROXY_RESPONSE_UNKNOWN, PUSH_PROXY_STATUS_NOT_AVAILABLE, PUSH_PROXY_STATUS_UNKNOWN, PUSH_PROXY_STATUS_VERIFIED} from '@constants/push_proxy'; @@ -22,7 +22,7 @@ import {getDeviceToken} from '@queries/app/global'; import {queryAllChannelsForTeam, queryChannelsById} from '@queries/servers/channel'; import {prepareModels, truncateCrtRelatedTables} from '@queries/servers/entry'; import {getHasCRTChanged} from '@queries/servers/preference'; -import {getConfig, getCurrentChannelId, getCurrentTeamId, getPushVerificationStatus, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; +import {getConfig, getCurrentChannelId, getCurrentTeamId, getIsDataRetentionEnabled, getPushVerificationStatus, getWebSocketLastDisconnected, setCurrentTeamAndChannelId} from '@queries/servers/system'; import {deleteMyTeams, getAvailableTeamIds, getTeamChannelHistory, queryMyTeams, queryMyTeamsByIds, queryTeamsById} from '@queries/servers/team'; import NavigationStore from '@store/navigation_store'; import {isDMorGM, sortChannelsByDisplayName} from '@utils/channel'; @@ -63,13 +63,37 @@ export type EntryResponse = { const FETCH_MISSING_DM_TIMEOUT = 2500; export const FETCH_UNREADS_TIMEOUT = 2500; -export const getRemoveTeamIds = async (database: Database, teamData: MyTeamsRequest) => { +export const entry = async (serverUrl: string, teamId?: string, channelId?: string, since = 0): Promise => { + const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); + const result = entryRest(serverUrl, teamId, channelId, since); + + // Fetch data retention policies + const isDataRetentionEnabled = await getIsDataRetentionEnabled(database); + if (isDataRetentionEnabled) { + fetchDataRetentionPolicy(serverUrl); + } + + return result; +}; + +export async function deferredAppEntryActions( + serverUrl: string, since: number, currentUserId: string, currentUserLocale: string, preferences: PreferenceType[] | undefined, + config: ClientConfig, license: ClientLicense | undefined, teamData: MyTeamsRequest, chData: MyChannelsRequest | undefined, + initialTeamId?: string, initialChannelId?: string) { + const result = restDeferredAppEntryActions(serverUrl, since, currentUserId, currentUserLocale, preferences, config, license, teamData, chData, initialTeamId, initialChannelId); + + autoUpdateTimezone(serverUrl); + + return result; +} + +const getRemoveTeamIds = async (database: Database, teamData: MyTeamsRequest) => { const myTeams = await queryMyTeams(database).fetch(); const joinedTeams = new Set(teamData.memberships?.filter((m) => m.delete_at === 0).map((m) => m.team_id)); return myTeams.filter((m) => !joinedTeams.has(m.id)).map((m) => m.id); }; -export const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[]) => { +const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[]) => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { return []; @@ -90,7 +114,7 @@ export const teamsToRemove = async (serverUrl: string, removeTeamIds?: string[]) return []; }; -export const entryRest = async (serverUrl: string, teamId?: string, channelId?: string, since = 0): Promise => { +const entryRest = async (serverUrl: string, teamId?: string, channelId?: string, since = 0): Promise => { const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; if (!operator) { return {error: `${serverUrl} database not found`}; @@ -137,7 +161,7 @@ export const entryRest = async (serverUrl: string, teamId?: string, channelId?: return {models: models.flat(), initialChannelId, initialTeamId, prefData, teamData, chData, meData}; }; -export const fetchAppEntryData = async (serverUrl: string, sinceArg: number, initialTeamId = ''): Promise => { +const fetchAppEntryData = async (serverUrl: string, sinceArg: number, initialTeamId = ''): Promise => { const database = DatabaseManager.serverDatabases[serverUrl]?.database; if (!database) { return {error: `${serverUrl} database not found`}; @@ -244,7 +268,7 @@ export const fetchAppEntryData = async (serverUrl: string, sinceArg: number, ini return data; }; -export const fetchAlternateTeamData = async ( +const fetchAlternateTeamData = async ( serverUrl: string, availableTeamIds: string[], removeTeamIds: string[], includeDeleted = true, since = 0, fetchOnly = false, isCRTEnabled?: boolean) => { let initialTeamId = ''; @@ -269,7 +293,7 @@ export const fetchAlternateTeamData = async ( return {initialTeamId, removeTeamIds}; }; -export async function entryInitialChannelId(database: Database, requestedChannelId = '', requestedTeamId = '', initialTeamId: string, locale: string, channels?: Channel[], memberships?: ChannelMember[]) { +async function entryInitialChannelId(database: Database, requestedChannelId = '', requestedTeamId = '', initialTeamId: string, locale: string, channels?: Channel[], memberships?: ChannelMember[]) { const membershipIds = new Set(memberships?.map((m) => m.channel_id)); const requestedChannel = channels?.find((c) => (c.id === requestedChannelId) && membershipIds.has(c.id)); @@ -307,7 +331,7 @@ export async function entryInitialChannelId(database: Database, requestedChannel return myFirstTeamChannel?.id || ''; } -export async function restDeferredAppEntryActions( +async function restDeferredAppEntryActions( serverUrl: string, since: number, currentUserId: string, currentUserLocale: string, preferences: PreferenceType[] | undefined, config: ClientConfig, license: ClientLicense | undefined, teamData: MyTeamsRequest, chData: MyChannelsRequest | undefined, initialTeamId?: string, initialChannelId?: string) { diff --git a/app/actions/remote/entry/gql_common.ts b/app/actions/remote/entry/gql_common.ts deleted file mode 100644 index acecb84d0..000000000 --- a/app/actions/remote/entry/gql_common.ts +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {storeConfigAndLicense} from '@actions/local/systems'; -import {fetchGroupsForMember} from '@actions/remote/groups'; -import {fetchPostsForUnreadChannels} from '@actions/remote/post'; -import {fetchDataRetentionPolicy} from '@actions/remote/systems'; -import {type MyTeamsRequest, updateCanJoinTeams} from '@actions/remote/team'; -import {syncTeamThreads} from '@actions/remote/thread'; -import {autoUpdateTimezone, fetchProfilesInGroupChannels, updateAllUsersSince} from '@actions/remote/user'; -import {gqlEntry, gqlEntryChannels, gqlOtherChannels} from '@client/graphQL/entry'; -import {General, Preferences} from '@constants'; -import DatabaseManager from '@database/manager'; -import {getPreferenceValue} from '@helpers/api/preference'; -import {selectDefaultTeam} from '@helpers/api/team'; -import {queryAllChannels, queryAllChannelsForTeam} from '@queries/servers/channel'; -import {prepareModels, truncateCrtRelatedTables} from '@queries/servers/entry'; -import {getHasCRTChanged} from '@queries/servers/preference'; -import {getConfig, getIsDataRetentionEnabled} from '@queries/servers/system'; -import {getFullErrorMessage} from '@utils/errors'; -import {filterAndTransformRoles, getMemberChannelsFromGQLQuery, getMemberTeamsFromGQLQuery, gqlToClientChannelMembership, gqlToClientPreference, gqlToClientSidebarCategory, gqlToClientTeamMembership, gqlToClientUser} from '@utils/graphql'; -import {logDebug} from '@utils/log'; -import {processIsCRTEnabled} from '@utils/thread'; - -import {teamsToRemove, FETCH_UNREADS_TIMEOUT, entryRest, type EntryResponse, entryInitialChannelId, restDeferredAppEntryActions, getRemoveTeamIds} from './common'; - -import type {MyChannelsRequest} from '@actions/remote/channel'; -import type {Database} from '@nozbe/watermelondb'; -import type ChannelModel from '@typings/database/models/servers/channel'; - -const FETCH_MISSING_GM_TIMEOUT = 2500; - -export async function deferredAppEntryGraphQLActions( - serverUrl: string, - since: number, - currentUserId: string, - teamData: MyTeamsRequest, - chData: MyChannelsRequest | undefined, - preferences: PreferenceType[] | undefined, - config: ClientConfig, - initialTeamId?: string, - initialChannelId?: string, -) { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - const {database} = operator; - - setTimeout(() => { - if (chData?.channels?.length && chData.memberships?.length) { - // defer fetching posts for unread channels on initial team - fetchPostsForUnreadChannels(serverUrl, chData.channels, chData.memberships, initialChannelId); - } - }, FETCH_UNREADS_TIMEOUT); - - if (preferences && processIsCRTEnabled(preferences, config.CollapsedThreads, config.FeatureFlagCollapsedThreads, config.Version)) { - if (initialTeamId) { - await syncTeamThreads(serverUrl, initialTeamId); - } - - if (teamData.teams?.length) { - for await (const team of teamData.teams) { - if (team.id !== initialTeamId) { - // need to await here since GM/DM threads in different teams overlap - await syncTeamThreads(serverUrl, team.id); - } - } - } - } - - if (initialTeamId) { - const result = await getChannelData(serverUrl, initialTeamId, currentUserId, true); - if ('error' in result) { - return result; - } - - const removeChannels = await getRemoveChannels(database, result.chData, initialTeamId, false); - - const modelPromises = await prepareModels({operator, removeChannels, chData: result.chData}, true); - - const roles = filterAndTransformRoles(result.roles); - if (roles.length) { - modelPromises.push(operator.handleRole({roles, prepareRecordsOnly: true})); - } - const models = (await Promise.all(modelPromises)).flat(); - operator.batchRecords(models, 'deferredAppEntryActions'); - - setTimeout(() => { - if (result.chData?.channels?.length && result.chData.memberships?.length) { - // defer fetching posts for unread channels on other teams - fetchPostsForUnreadChannels(serverUrl, result.chData.channels, result.chData.memberships, initialChannelId); - } - }, FETCH_UNREADS_TIMEOUT); - } - - // Fetch groups for current user - fetchGroupsForMember(serverUrl, currentUserId); - - updateCanJoinTeams(serverUrl); - updateAllUsersSince(serverUrl, since); - - // defer sidebar GM profiles - setTimeout(async () => { - const gmIds = chData?.channels?.reduce>((acc, v) => { - if (v?.type === General.GM_CHANNEL) { - acc.add(v.id); - } - return acc; - }, new Set()); - if (gmIds?.size) { - fetchProfilesInGroupChannels(serverUrl, Array.from(gmIds)); - } - }, FETCH_MISSING_GM_TIMEOUT); - - return {error: undefined}; -} - -const getRemoveChannels = async (database: Database, chData: MyChannelsRequest | undefined, initialTeamId: string, singleTeam: boolean) => { - const removeChannels: ChannelModel[] = []; - if (chData?.channels) { - const fetchedChannelIds = chData.channels?.map((channel) => channel.id); - - const query = singleTeam ? queryAllChannelsForTeam(database, initialTeamId) : queryAllChannels(database); - const channels = await query.fetch(); - - for (const channel of channels) { - const excludeCondition = singleTeam ? true : channel.teamId !== initialTeamId && channel.teamId !== ''; - if (excludeCondition && !fetchedChannelIds?.includes(channel.id)) { - removeChannels.push(channel); - } - } - } - - return removeChannels; -}; - -const getChannelData = async (serverUrl: string, initialTeamId: string, userId: string, exclude: boolean): Promise<{chData: MyChannelsRequest; roles: Array|undefined>} | {error: unknown}> => { - let response; - try { - const request = exclude ? gqlOtherChannels : gqlEntryChannels; - response = await request(serverUrl, initialTeamId); - } catch (error) { - return {error: getFullErrorMessage(error)}; - } - - if ('error' in response) { - return {error: response.error}; - } - - if ('errors' in response && response.errors?.length) { - return {error: response.errors[0].message}; - } - - const channelsFetchedData = response.data; - - const chData = { - channels: getMemberChannelsFromGQLQuery(channelsFetchedData), - memberships: channelsFetchedData.channelMembers?.map((m) => gqlToClientChannelMembership(m, userId)), - categories: channelsFetchedData.sidebarCategories?.map((c) => gqlToClientSidebarCategory(c, '')), - }; - const roles = channelsFetchedData.channelMembers?.map((m) => m.roles).flat() || []; - - return {chData, roles}; -}; - -export const entryGQL = async (serverUrl: string, currentTeamId?: string, currentChannelId?: string): Promise => { - const operator = DatabaseManager.serverDatabases[serverUrl]?.operator; - if (!operator) { - return {error: `${serverUrl} database not found`}; - } - const {database} = operator; - - let response; - try { - response = await gqlEntry(serverUrl); - } catch (error) { - return {error: getFullErrorMessage(error)}; - } - - if ('error' in response) { - return {error: response.error}; - } - - if ('errors' in response && response.errors?.length) { - return {error: response.errors[0].message}; - } - - const fetchedData = response.data; - - const config = fetchedData.config || {} as ClientConfig; - const license = fetchedData.license || {} as ClientLicense; - await storeConfigAndLicense(serverUrl, config, license); - - const meData = { - user: gqlToClientUser(fetchedData.user!), - }; - - const allTeams = getMemberTeamsFromGQLQuery(fetchedData); - const allTeamMemberships = fetchedData.teamMembers.map((m) => gqlToClientTeamMembership(m, meData.user.id)); - - const [nonArchivedTeams, archivedTeamIds] = allTeams.reduce((acc, t) => { - if (t.delete_at) { - acc[1].add(t.id); - return acc; - } - return [[...acc[0], t], acc[1]]; - }, [[], new Set()]); - - const nonArchivedTeamMemberships = allTeamMemberships.filter((m) => !archivedTeamIds.has(m.team_id)); - - const teamData = { - teams: nonArchivedTeams, - memberships: nonArchivedTeamMemberships, - }; - - const prefData = { - preferences: fetchedData.user?.preferences?.map(gqlToClientPreference), - }; - - if (prefData.preferences) { - const crtToggled = await getHasCRTChanged(database, prefData.preferences); - if (crtToggled) { - const {error} = await truncateCrtRelatedTables(serverUrl); - if (error) { - return {error: `Resetting CRT on ${serverUrl} failed`}; - } - } - } - - let initialTeamId = currentTeamId; - if (!teamData.teams.length) { - initialTeamId = ''; - } else if (!initialTeamId || !teamData.teams.find((t) => t.id === currentTeamId && t.delete_at === 0)) { - const teamOrderPreference = getPreferenceValue(prefData.preferences || [], Preferences.CATEGORIES.TEAMS_ORDER, '', ''); - initialTeamId = selectDefaultTeam(teamData.teams, meData.user.locale, teamOrderPreference, config.ExperimentalPrimaryTeam)?.id || ''; - } - const gqlRoles = [ - ...fetchedData.user?.roles || [], - ...fetchedData.teamMembers?.map((m) => m.roles).flat() || [], - ]; - - let chData; - if (initialTeamId) { - const result = await getChannelData(serverUrl, initialTeamId, meData.user.id, false); - if ('error' in result) { - return result; - } - - chData = result.chData; - gqlRoles.push(...result.roles); - } - - const roles = filterAndTransformRoles(gqlRoles); - - const initialChannelId = await entryInitialChannelId(database, currentChannelId, currentTeamId, initialTeamId, meData.user.id, chData?.channels, chData?.memberships); - const removeChannels = await getRemoveChannels(database, chData, initialTeamId, true); - const removeTeamIds = await getRemoveTeamIds(database, teamData); - const removeTeams = await teamsToRemove(serverUrl, removeTeamIds); - - const modelPromises = await prepareModels({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData}, true); - if (roles.length) { - modelPromises.push(operator.handleRole({roles, prepareRecordsOnly: true})); - } - const models = (await Promise.all(modelPromises)).flat(); - return {models, initialTeamId, initialChannelId, prefData, teamData, chData, meData}; -}; - -export const entry = async (serverUrl: string, teamId?: string, channelId?: string, since = 0): Promise => { - const {database} = DatabaseManager.getServerDatabaseAndOperator(serverUrl); - const config = await getConfig(database); - let result; - if (config?.FeatureFlagGraphQL === 'true') { - result = await entryGQL(serverUrl, teamId, channelId); - if ('error' in result) { - logDebug('Error using GraphQL, trying REST', result.error); - result = entryRest(serverUrl, teamId, channelId, since); - } - } else { - result = entryRest(serverUrl, teamId, channelId, since); - } - - // Fetch data retention policies - const isDataRetentionEnabled = await getIsDataRetentionEnabled(database); - if (isDataRetentionEnabled) { - fetchDataRetentionPolicy(serverUrl); - } - - return result; -}; - -export async function deferredAppEntryActions( - serverUrl: string, since: number, currentUserId: string, currentUserLocale: string, preferences: PreferenceType[] | undefined, - config: ClientConfig, license: ClientLicense | undefined, teamData: MyTeamsRequest, chData: MyChannelsRequest | undefined, - initialTeamId?: string, initialChannelId?: string) { - let result; - if (config?.FeatureFlagGraphQL === 'true') { - result = await deferredAppEntryGraphQLActions(serverUrl, since, currentUserId, teamData, chData, preferences, config, initialTeamId, initialChannelId); - if (result.error) { - logDebug('Error using GraphQL, trying REST', result.error); - result = restDeferredAppEntryActions(serverUrl, since, currentUserId, currentUserLocale, preferences, config, license, teamData, chData, initialTeamId, initialChannelId); - } - } else { - result = restDeferredAppEntryActions(serverUrl, since, currentUserId, currentUserLocale, preferences, config, license, teamData, chData, initialTeamId, initialChannelId); - } - - autoUpdateTimezone(serverUrl); - - return result; -} diff --git a/app/actions/websocket/index.ts b/app/actions/websocket/index.ts index 275faafc3..51467e5aa 100644 --- a/app/actions/websocket/index.ts +++ b/app/actions/websocket/index.ts @@ -4,8 +4,7 @@ import {markChannelAsViewed} from '@actions/local/channel'; import {dataRetentionCleanup} from '@actions/local/systems'; import {markChannelAsRead} from '@actions/remote/channel'; -import {handleEntryAfterLoadNavigation, registerDeviceToken} from '@actions/remote/entry/common'; -import {deferredAppEntryActions, entry} from '@actions/remote/entry/gql_common'; +import {deferredAppEntryActions, entry, handleEntryAfterLoadNavigation, registerDeviceToken} from '@actions/remote/entry/common'; import {fetchPostsForChannel, fetchPostThread} from '@actions/remote/post'; import {openAllUnreadChannels} from '@actions/remote/preference'; import {autoUpdateTimezone} from '@actions/remote/user'; diff --git a/app/client/graphQL/constants.ts b/app/client/graphQL/constants.ts deleted file mode 100644 index 21328232b..000000000 --- a/app/client/graphQL/constants.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -export const QUERY_ENTRY = 'gql_m_entry'; -export const QUERY_CHANNELS = 'gql_m_channels'; -export const QUERY_CHANNELS_NEXT = 'gql_m_channels_next'; -export const QUERY_ALL_CHANNELS = 'gql_m_all_channels'; -export const QUERY_ALL_CHANNELS_NEXT = 'gql_m_all_channels_next'; - -export default { - QUERY_ENTRY, - QUERY_CHANNELS, - QUERY_CHANNELS_NEXT, - QUERY_ALL_CHANNELS, - QUERY_ALL_CHANNELS_NEXT, -}; diff --git a/app/client/graphQL/entry.ts b/app/client/graphQL/entry.ts deleted file mode 100644 index 80e6575ce..000000000 --- a/app/client/graphQL/entry.ts +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -import {MEMBERS_PER_PAGE} from '@constants/graphql'; -import NetworkManager from '@managers/network_manager'; -import {getFullErrorMessage} from '@utils/errors'; -import {logDebug} from '@utils/log'; - -import QueryNames from './constants'; - -const doGQLQuery = async (serverUrl: string, query: string, variables: {[name: string]: any}, operationName: string) => { - try { - const client = NetworkManager.getClient(serverUrl); - const response = await client.doFetch('/api/v5/graphql', {method: 'post', body: {query, variables, operationName}}) as GQLResponse; - return response; - } catch (error) { - logDebug('error on doGQLQuery', getFullErrorMessage(error)); - return {error}; - } -}; - -export const gqlEntry = async (serverUrl: string) => { - return doGQLQuery(serverUrl, entryQuery, {}, QueryNames.QUERY_ENTRY); -}; - -export const gqlEntryChannels = async (serverUrl: string, teamId: string) => { - const variables = { - teamId, - exclude: false, - perPage: MEMBERS_PER_PAGE, - }; - const response = await doGQLQuery(serverUrl, channelsQuery, variables, QueryNames.QUERY_CHANNELS); - if ('error' in response || response.errors) { - return response; - } - - let members = response.data.channelMembers; - - while (members?.length === MEMBERS_PER_PAGE) { - let pageResponse; - try { - // eslint-disable-next-line no-await-in-loop - pageResponse = await gqlEntryChannelsNextPage(serverUrl, teamId, members[members.length - 1].cursor!, false); - } catch { - break; - } - - if ('error' in pageResponse) { - break; - } - - members = pageResponse.data.channelMembers!; - response.data.channelMembers?.push(...members); - } - - return response; -}; -const gqlEntryChannelsNextPage = async (serverUrl: string, teamId: string, cursor: string, exclude: boolean) => { - const variables = { - teamId, - exclude, - perPage: MEMBERS_PER_PAGE, - cursor, - }; - return doGQLQuery(serverUrl, nextPageChannelsQuery, variables, QueryNames.QUERY_CHANNELS_NEXT); -}; - -export const gqlOtherChannels = async (serverUrl: string, teamId: string) => { - const variables = { - teamId, - exclude: true, - perPage: MEMBERS_PER_PAGE, - }; - const response = await doGQLQuery(serverUrl, channelsQuery, variables, QueryNames.QUERY_CHANNELS); - if ('error' in response || response.errors) { - return response; - } - - let members = response.data.channelMembers; - - while (members?.length === MEMBERS_PER_PAGE) { - let pageResponse; - try { - // eslint-disable-next-line no-await-in-loop - pageResponse = await gqlEntryChannelsNextPage(serverUrl, teamId, members[members.length - 1].cursor!, true); - } catch { - break; - } - - if ('error' in pageResponse || 'errors' in pageResponse) { - break; - } - - members = pageResponse.data.channelMembers!; - response.data.channelMembers?.push(...members); - } - - return response; -}; - -export const gqlAllChannels = async (serverUrl: string) => { - const variables = { - perPage: MEMBERS_PER_PAGE, - }; - const response = await doGQLQuery(serverUrl, allChannelsQuery, variables, QueryNames.QUERY_ALL_CHANNELS); - if ('error' in response || response.errors) { - return response; - } - - let members = response.data.channelMembers; - - while (members?.length === MEMBERS_PER_PAGE) { - let pageResponse; - try { - // eslint-disable-next-line no-await-in-loop - pageResponse = await gqlAllChannelsNextPage(serverUrl, members[members.length - 1].cursor!); - } catch { - break; - } - - if ('error' in pageResponse || 'errors' in pageResponse) { - break; - } - - members = pageResponse.data.channelMembers!; - response.data.channelMembers?.push(...members); - } - - return response; -}; - -const gqlAllChannelsNextPage = async (serverUrl: string, cursor: string) => { - const variables = { - perPage: MEMBERS_PER_PAGE, - cursor, - }; - return doGQLQuery(serverUrl, nextPageAllChannelsQuery, variables, QueryNames.QUERY_ALL_CHANNELS_NEXT); -}; - -const entryQuery = ` -query ${QueryNames.QUERY_ENTRY} { - config - license - user(id:"me") { - id - createAt - updateAt - deleteAt - username - authService - email - emailVerified - nickname - firstName - lastName - position - roles { - id - name - permissions - } - locale - notifyProps - props - timezone - isBot - lastPictureUpdate - remoteId - status { - status - } - botDescription - botLastIconUpdate - preferences{ - category - name - value - userId - } - sessions { - createAt - expiresAt - } - termsOfServiceId - termsOfServiceCreateAt - } - teamMembers(userId:"me") { - deleteAt - schemeAdmin - roles { - id - name - permissions - } - team { - id - description - displayName - name - type - allowedDomains - lastTeamIconUpdate - groupConstrained - allowOpenInvite - createAt - updateAt - deleteAt - schemeId - policyId - cloudLimitsArchived - } - } -} -`; - -const channelsQuery = ` -query ${QueryNames.QUERY_CHANNELS}($teamId: String!, $perPage: Int!, $exclude: Boolean!) { - channelMembers(userId:"me", first:$perPage, teamId:$teamId, excludeTeam:$exclude) { - cursor - msgCount - msgCountRoot - mentionCount - mentionCountRoot - schemeAdmin - lastViewedAt - notifyProps - roles { - id - name - permissions - } - channel { - id - header - purpose - type - createAt - updateAt - creatorId - deleteAt - displayName - prettyDisplayName - groupConstrained - name - shared - lastPostAt - totalMsgCount - totalMsgCountRoot - lastRootPostAt - team { - id - } - } - } - sidebarCategories(userId:"me", teamId:$teamId, excludeTeam:$exclude) { - displayName - id - sortOrder - sorting - type - muted - collapsed - channelIds - teamId - } -} -`; - -const nextPageChannelsQuery = ` -query ${QueryNames.QUERY_CHANNELS_NEXT}($teamId: String!, $perPage: Int!, $exclude: Boolean!, $cursor: String!) { - channelMembers(userId:"me", first:$perPage, after:$cursor, teamId:$teamId, excludeTeam:$exclude) { - cursor - msgCount - msgCountRoot - mentionCount - mentionCountRoot - schemeAdmin - lastViewedAt - notifyProps - roles { - id - name - permissions - } - channel { - id - header - purpose - type - createAt - updateAt - creatorId - deleteAt - displayName - prettyDisplayName - groupConstrained - name - shared - lastPostAt - totalMsgCount - totalMsgCountRoot - lastRootPostAt - team { - id - } - } - } -} -`; - -const allChannelsQuery = ` -query ${QueryNames.QUERY_ALL_CHANNELS}($perPage: Int!){ - channelMembers(userId:"me", first:$perPage) { - cursor - msgCount - msgCountRoot - mentionCount - mentionCountRoot - schemeAdmin - lastViewedAt - notifyProps - roles { - id - name - permissions - } - channel { - id - header - purpose - type - createAt - updateAt - creatorId - deleteAt - displayName - prettyDisplayName - groupConstrained - name - shared - lastPostAt - totalMsgCount - totalMsgCountRoot - lastRootPostAt - team { - id - } - } - } -} -`; - -const nextPageAllChannelsQuery = ` -query ${QueryNames.QUERY_ALL_CHANNELS_NEXT}($perPage: Int!, $cursor: String!) { - channelMembers(userId:"me", first:$perPage, after:$cursor) { - cursor - msgCount - msgCountRoot - mentionCount - mentionCountRoot - schemeAdmin - lastViewedAt - notifyProps - roles { - id - name - permissions - } - channel { - id - header - purpose - type - createAt - updateAt - creatorId - deleteAt - displayName - prettyDisplayName - groupConstrained - name - shared - lastPostAt - totalMsgCount - totalMsgCountRoot - lastRootPostAt - team { - id - } - } - } -} -`; diff --git a/app/constants/graphql.ts b/app/constants/graphql.ts deleted file mode 100644 index 99e36be96..000000000 --- a/app/constants/graphql.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -export const MEMBERS_PER_PAGE = 200; diff --git a/app/queries/servers/channel.ts b/app/queries/servers/channel.ts index 222a9ff88..71c9b2225 100644 --- a/app/queries/servers/channel.ts +++ b/app/queries/servers/channel.ts @@ -67,17 +67,17 @@ export function prepareMissingChannelsForAllTeams(operator: ServerDataOperator, } } -export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, teamId: string, channels: Channel[], channelMembers: ChannelMembership[], isCRTEnabled?: boolean, isGraphQL = false) => { +export const prepareMyChannelsForTeam = async (operator: ServerDataOperator, teamId: string, channels: Channel[], channelMembers: ChannelMembership[], isCRTEnabled?: boolean) => { const {database} = operator; - const channelsQuery = isGraphQL ? queryAllChannels(database) : queryAllChannelsForTeam(database, teamId); + const channelsQuery = queryAllChannelsForTeam(database, teamId); const allChannelsForTeam = (await channelsQuery.fetch()). reduce((map: Record, channel) => { map[channel.id] = channel; return map; }, {}); - const channelInfosQuery = isGraphQL ? queryAllChannelsInfo(database) : queryAllChannelsInfoForTeam(database, teamId); + const channelInfosQuery = queryAllChannelsInfoForTeam(database, teamId); const allChannelsInfoForTeam = (await channelInfosQuery.fetch()). reduce((map: Record, info) => { map[info.id] = info; diff --git a/app/queries/servers/entry.ts b/app/queries/servers/entry.ts index 23231d519..d237035c6 100644 --- a/app/queries/servers/entry.ts +++ b/app/queries/servers/entry.ts @@ -43,7 +43,7 @@ const { MY_CHANNEL, } = MM_TABLES.SERVER; -export async function prepareModels({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData, isCRTEnabled}: PrepareModelsArgs, isGraphQL = false): Promise>> { +export async function prepareModels({operator, initialTeamId, removeTeams, removeChannels, teamData, chData, prefData, meData, isCRTEnabled}: PrepareModelsArgs): Promise>> { const modelPromises: Array> = []; if (removeTeams?.length) { @@ -67,10 +67,8 @@ export async function prepareModels({operator, initialTeamId, removeTeams, remov } if (chData?.channels?.length && chData.memberships?.length) { - if (isGraphQL) { - modelPromises.push(...await prepareMyChannelsForTeam(operator, '', chData.channels, chData.memberships, isCRTEnabled, true)); - } else if (initialTeamId) { - modelPromises.push(...await prepareMyChannelsForTeam(operator, initialTeamId, chData.channels, chData.memberships, isCRTEnabled, false)); + if (initialTeamId) { + modelPromises.push(...await prepareMyChannelsForTeam(operator, initialTeamId, chData.channels, chData.memberships, isCRTEnabled)); } } diff --git a/app/utils/graphql.ts b/app/utils/graphql.ts deleted file mode 100644 index 6551975c5..000000000 --- a/app/utils/graphql.ts +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -const defaultNotifyProps: UserNotifyProps = { - channel: 'true', - comments: 'never', - desktop: 'mention', - desktop_sound: 'true', - email: 'true', - email_threads: 'all', - first_name: 'false', - mention_keys: '', - push: 'mention', - push_status: 'away', - push_threads: 'all', - -}; -export const gqlToClientUser = (u: Partial): UserProfile => { - return { - id: u.id || '', - create_at: u.createAt || 0, - update_at: u.updateAt || 0, - delete_at: u.deleteAt || 0, - username: u.username || '', - auth_service: u.authService || '', - - email: u.email || '', - email_verified: u.emailVerified ?? true, - nickname: u.nickname || '', - first_name: u.firstName || '', - last_name: u.lastName || '', - position: u.position || '', - roles: u.roles?.map((v) => v.name!).join(' ') || '', - locale: u.locale || '', - notify_props: u.notifyProps || defaultNotifyProps, - props: u.props, - - timezone: u.timezone, - is_bot: u.isBot, - last_picture_update: u.lastPictureUpdate, - remote_id: u.remoteId, - status: u.status?.status || '', - bot_description: u.botDescription, - bot_last_icon_update: u.botLastIconUpdate, - - terms_of_service_id: u.termsOfServiceId || '', - terms_of_service_create_at: u.termsOfServiceCreateAt || 0, - - auth_data: '', - }; -}; - -export const gqlToClientSession = (s: Partial): Session => { - return { - create_at: s.createAt || 0, - expires_at: s.expiresAt || 0, - id: '', - user_id: '', - }; -}; - -export const gqlToClientTeamMembership = (m: Partial, userId?: string): TeamMembership => { - return { - team_id: m.team?.id || '', - delete_at: m.deleteAt || 0, - roles: m.roles?.map((v) => v.name!).join(' ') || '', - user_id: m.user?.id || userId || '', - scheme_admin: m.schemeAdmin || false, - scheme_user: m.schemeUser || false, - mention_count: 0, - msg_count: 0, - }; -}; - -export const gqlToClientSidebarCategory = (c: Partial, teamId: string): CategoryWithChannels => { - return { - channel_ids: c.channelIds || [], - collapsed: c.collapsed || false, - display_name: c.displayName || '', - id: c.id || '', - muted: c.muted || false, - sort_order: c.sortOrder || 0, - sorting: c.sorting || 'alpha', - team_id: c.teamId || teamId, - type: c.type || 'channels', - }; -}; - -export const gqlToClientTeam = (t: Partial): Team => { - return { - allow_open_invite: t.allowOpenInvite || false, - allowed_domains: t.allowedDomains || '', - company_name: t.companyName || '', - create_at: t.createAt || 0, - delete_at: t.deleteAt || 0, - description: t.description || '', - display_name: t.displayName || '', - email: t.email || '', - group_constrained: t.groupConstrained || false, - id: t.id || '', - invite_id: t.inviteId || '', - last_team_icon_update: t.lastTeamIconUpdate || 0, - name: t.name || '', - scheme_id: t.schemeId || '', - type: t.type || 'I', - update_at: t.updateAt || 0, - - // cloudLimitsArchived and policyId not used - }; -}; - -export const gqlToClientPreference = (p: Partial): PreferenceType => { - return { - category: p.category || '', - name: p.name || '', - user_id: p.userId || '', - value: p.value || '', - }; -}; - -export const gqlToClientChannelMembership = (m: Partial, userId?: string): ChannelMembership => { - return { - channel_id: m.channel?.id || '', - last_update_at: m.lastUpdateAt || 0, - last_viewed_at: m.lastViewedAt || 0, - mention_count: m.mentionCount || 0, - mention_count_root: m.mentionCountRoot || 0, - msg_count: m.msgCount || 0, - msg_count_root: m.msgCountRoot || 0, - notify_props: m.notifyProps || {}, - roles: m.roles?.map((r) => r.name).join(' ') || '', - user_id: m.user?.id || userId || '', - is_unread: false, - last_post_at: 0, - post_root_id: '', - scheme_admin: m.schemeAdmin, - scheme_user: m.schemeUser, - }; -}; - -export const gqlToClientChannel = (c: Partial, teamId?: string): Channel => { - return { - create_at: c.createAt || 0, - creator_id: c.creatorId || '', - delete_at: c.deleteAt || 0, - display_name: c.prettyDisplayName || c.displayName || '', - extra_update_at: 0, - group_constrained: c.groupConstrained || false, - header: c.header || '', - id: c.id || '', - last_post_at: c.lastPostAt || 0, - last_root_post_at: c.lastRootPostAt || 0, - name: c.name || '', - purpose: c.purpose || '', - scheme_id: c.schemeId || '', - shared: c.shared || false, - team_id: c.team?.id || teamId || '', - total_msg_count: c.totalMsgCount || 0, - total_msg_count_root: c.totalMsgCountRoot || 0, - type: c.type || 'O', - update_at: c.updateAt || 0, - fake: false, - isCurrent: false, - status: '', - teammate_id: '', - }; -}; - -export const gqlToClientChannelStats = (s: Partial): ChannelStats => { - return { - channel_id: s.id || '', - guest_count: s.stats?.guestCount || 0, - member_count: s.stats?.memberCount || 0, - pinnedpost_count: s.stats?.pinnePostCount || 0, - files_count: s.stats?.filesCount || 0, - }; -}; - -export const gqlToClientRole = (r: Partial): Role => { - return { - id: r.id || '', - name: r.name || '', - permissions: r.permissions || [], - built_in: r.builtIn, - scheme_managed: r.schemeManaged, - }; -}; - -export const getMemberChannelsFromGQLQuery = (data: GQLData) => { - return data.channelMembers?.reduce((acc, m) => { - if (m.channel) { - acc.push(gqlToClientChannel(m.channel)); - } - return acc; - }, []); -}; - -export const getMemberTeamsFromGQLQuery = (data: GQLData) => { - return data.teamMembers?.reduce((acc, m) => { - if (m.team) { - acc.push(gqlToClientTeam(m.team)); - } - return acc; - }, []); -}; - -export const filterAndTransformRoles = (roles: Array | undefined>) => { - const byName = roles.reduce<{[name: string]: Partial}>((acum, r) => { - if (r?.name && !acum[r.name]) { - acum[r.name] = r; - } - return acum; - }, {}); - return Object.values(byName).map((r) => gqlToClientRole(r)); -}; diff --git a/types/api/config.d.ts b/types/api/config.d.ts index 7320a732d..403993b97 100644 --- a/types/api/config.d.ts +++ b/types/api/config.d.ts @@ -119,7 +119,6 @@ interface ClientConfig { ExtendSessionLengthWithActivity: string; FeatureFlagAppsEnabled?: string; FeatureFlagCollapsedThreads?: string; - FeatureFlagGraphQL?: string; FeatureFlagPostPriority?: string; ForgotPasswordLink?: string; GfycatApiKey: string; diff --git a/types/api/graphql.d.ts b/types/api/graphql.d.ts deleted file mode 100644 index 56b16b3ae..000000000 --- a/types/api/graphql.d.ts +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -type GQLResponse = { - errors?: GQLError[]; - data: GQLData; -} - -type GQLData = { - user?: Partial; - config?: ClientConfig; - license?: ClientLicense; - teamMembers: Array>; - channels?: Array>; - channelsLeft?: Array>; - channelMembers?: Array>; - sidebarCategories?: Array>; -} - -type GQLError = { - message: string; - path: Array; -} - -type GQLUser = { - id: string; - createAt: number; - updateAt: number; - deleteAt: number; - username: string; - authService: string; - email: string; - emailVerified: boolean; - nickname: string; - firstName: string; - lastName: string; - position: string; - locale: string; - notifyProps: UserNotifyProps; - props: UserProps; - timezone: UserTimezone; - isBot: boolean; - lastPictureUpdate: number; - remoteId: string; - botDescription: string; - botLastIconUpdate: number; - termsOfServiceId: string; - termsOfServiceCreateAt: number; - - roles: Array>; - customStatus: Partial; - status: Partial; - preferences: Array>; - sessions: Array>; - - // Derived - isSystemAdmin: boolean; - isGuest: boolean; -} - -type GQLSession = { - createAt: number; - expiresAt: number; -} - -type GQLTeamMembership = { - team: Partial; - user: Partial; - roles: Array>; - deleteAt: number; - schemeGuest: boolean; - schemeUser: boolean; - schemeAdmin: boolean; -} - -type GQLSidebarCategory = { - id: string; - sorting: CategorySorting; - type: CategoryType; - displayName: string; - muted: boolean; - collapsed: boolean; - channelIds: string[]; - sortOrder: number; - teamId: string; -} - -type GQLTeam = { - id: string; - displayName: string; - name: string; - description: string; - email: string; - type: TeamType; - companyName: string; - allowedDomains: string; - inviteId: string; - lastTeamIconUpdate: number; - groupConstrained: boolean; - allowOpenInvite: boolean; - updateAt: number; - createAt: number; - deleteAt: number; - schemeId: string; - policyId: string; - cloudLimitsArchived: boolean; -} - -type GQLUserCustomStatus = { - emoji: string; - text: string; - duration: CustomStatusDuration; - expiresAt: string; -} - -type GQLUserStatus = { - status: string; - manual: boolean; - lastActivityAt: number; - activeChannel: string; - dndEndTime: number; -} - -type GQLPreference = { - userId: string; - category: string; - name: string; - value: string; -} - -type GQLChannelMembership = { - channel: Partial; - user: Partial; - roles: Array>; - lastViewedAt: number; - msgCount: number; - msgCountRoot: number; - mentionCount: number; - mentionCountRoot: number; - notifyProps: ChannelNotifyProps; - lastUpdateAt: number; - schemeGuest: boolean; - schemeUser: boolean; - schemeAdmin: boolean; - explicitRoles: string; - cursor: string; -} - -type GQLChannel = { - id: string; - createAt: number; - updateAt: number; - deleteAt: number; - type: ChannelType; - displayName: string; - prettyDisplayName: string; - name: string; - header: string; - purpose: string; - creatorId: string; - schemeId: string; - team: Partial; - cursor: string; - groupConstrained: boolean; - shared: boolean; - lastPostAt: number; - lastRootPostAt: number; - totalMsgCount: number; - totalMsgCountRoot: number; - stats: Partial; -} - -type GQLStats = { - guestCount: number; - memberCount: number; - pinnePostCount: number; - filesCount: number; -} - -type GQLRole = { - id: string; - name: string; - permissions: string[]; - schemeManaged: boolean; - builtIn: boolean; -}