diff --git a/codegen.json b/codegen.json index 46aa03a0329..9354c2d6426 100644 --- a/codegen.json +++ b/codegen.json @@ -105,7 +105,7 @@ "RequestToJoinDomainSuccess": "./types/RequestToJoinDomainSuccess#RequestToJoinDomainSuccessSource", "ResetReflectionGroupsSuccess": "./types/ResetReflectionGroupsSuccess#ResetReflectionGroupsSuccessSource", "RetroReflection": "../../database/types/RetroReflection#default as RetroReflectionDB", - "RetroReflectionGroup": "../../database/types/RetroReflectionGroup#default as RetroReflectionGroupDB", + "RetroReflectionGroup": "./types/RetroReflectionGroup#RetroReflectionGroupSource", "RetrospectiveMeeting": "../../database/types/MeetingRetrospective#default", "RetrospectiveMeetingMember": "../../database/types/RetroMeetingMember#default", "RetrospectiveMeetingSettings": "../../database/types/MeetingSettingsRetrospective#default", diff --git a/packages/client/modules/demo/ClientGraphQLServer.ts b/packages/client/modules/demo/ClientGraphQLServer.ts index e58c5f10f22..dd8adfd039f 100644 --- a/packages/client/modules/demo/ClientGraphQLServer.ts +++ b/packages/client/modules/demo/ClientGraphQLServer.ts @@ -15,7 +15,6 @@ import NewMeetingStage from '../../../server/database/types/GenericMeetingStage' import GoogleAnalyzedEntity from '../../../server/database/types/GoogleAnalyzedEntity' import ReflectPhase from '../../../server/database/types/ReflectPhase' import Reflection from '../../../server/database/types/Reflection' -import ReflectionGroup from '../../../server/database/types/ReflectionGroup' import ITask from '../../../server/database/types/Task' import { ExternalLinks, @@ -69,8 +68,17 @@ export type DemoReflection = Omit & { +export type DemoReflectionGroup = { __typename: string + id: string + isActive: boolean + meetingId: string + promptId: string + sortOrder: number + smartTitle: string | null + summary: string | null + title: string | null + discussionPromptQuestion: string | null commentors: any createdAt: string | Date meeting: any diff --git a/packages/embedder/workflows/helpers/publishSimilarRetroTopics.ts b/packages/embedder/workflows/helpers/publishSimilarRetroTopics.ts index f8de026db74..0f881ee4621 100644 --- a/packages/embedder/workflows/helpers/publishSimilarRetroTopics.ts +++ b/packages/embedder/workflows/helpers/publishSimilarRetroTopics.ts @@ -22,7 +22,7 @@ const makeSimilarDiscussionLink = async ( const {meetingId, discussionTopicId: reflectionGroupId} = discussion const [meeting, reflectionGroup] = await Promise.all([ dataLoader.get('newMeetings').load(meetingId), - dataLoader.get('retroReflectionGroups').load(reflectionGroupId) + dataLoader.get('retroReflectionGroups').loadNonNull(reflectionGroupId) ]) if (!meeting || !isRetroMeeting(meeting)) throw new Error('invalid meeting type') diff --git a/packages/server/database/rethinkDriver.ts b/packages/server/database/rethinkDriver.ts index e030375d971..40621c4db4c 100644 --- a/packages/server/database/rethinkDriver.ts +++ b/packages/server/database/rethinkDriver.ts @@ -30,7 +30,6 @@ import OrganizationUser from './types/OrganizationUser' import PasswordResetRequest from './types/PasswordResetRequest' import PushInvitation from './types/PushInvitation' import Reflection from './types/Reflection' -import ReflectionGroup from './types/ReflectionGroup' import RetrospectivePrompt from './types/RetrospectivePrompt' import SAML from './types/SAML' import SuggestedActionCreateNewTeam from './types/SuggestedActionCreateNewTeam' @@ -143,10 +142,6 @@ export type RethinkSchema = { type: MeetingTemplate index: 'teamId' | 'orgId' } - RetroReflectionGroup: { - type: ReflectionGroup - index: 'meetingId' - } RetroReflection: { type: Reflection index: 'meetingId' | 'reflectionGroupId' diff --git a/packages/server/dataloader/foreignKeyLoaderMakers.ts b/packages/server/dataloader/foreignKeyLoaderMakers.ts index 0c967c37b1a..8807ba06f17 100644 --- a/packages/server/dataloader/foreignKeyLoaderMakers.ts +++ b/packages/server/dataloader/foreignKeyLoaderMakers.ts @@ -23,3 +23,17 @@ export const embeddingsMetadataByRefId = foreignKeyLoaderMaker( return pg.selectFrom('EmbeddingsMetadata').selectAll().where('refId', 'in', refId).execute() } ) + +export const retroReflectionGroupsByMeetingId = foreignKeyLoaderMaker( + 'retroReflectionGroups', + 'meetingId', + async (meetingIds) => { + const pg = getKysely() + return pg + .selectFrom('RetroReflectionGroup') + .selectAll() + .where('meetingId', 'in', meetingIds) + .where('isActive', '=', true) + .execute() + } +) diff --git a/packages/server/dataloader/primaryKeyLoaderMakers.ts b/packages/server/dataloader/primaryKeyLoaderMakers.ts index 187d6802720..87c8adafc0a 100644 --- a/packages/server/dataloader/primaryKeyLoaderMakers.ts +++ b/packages/server/dataloader/primaryKeyLoaderMakers.ts @@ -23,3 +23,7 @@ export const domainJoinRequests = primaryKeyLoaderMaker(getDomainJoinRequestsByI export const embeddingsMetadata = primaryKeyLoaderMaker((ids: readonly number[]) => { return getKysely().selectFrom('EmbeddingsMetadata').selectAll().where('id', 'in', ids).execute() }) + +export const retroReflectionGroups = primaryKeyLoaderMaker((ids: readonly string[]) => { + return getKysely().selectFrom('RetroReflectionGroup').selectAll().where('id', 'in', ids).execute() +}) diff --git a/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts b/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts index 70a038b712c..34ad30aca6f 100644 --- a/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts +++ b/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts @@ -152,19 +152,6 @@ export const organizationUsersByUserId = new RethinkForeignKeyLoaderMaker( } ) -export const retroReflectionGroupsByMeetingId = new RethinkForeignKeyLoaderMaker( - 'retroReflectionGroups', - 'meetingId', - async (meetingIds) => { - const r = await getRethink() - return r - .table('RetroReflectionGroup') - .getAll(r.args(meetingIds), {index: 'meetingId'}) - .filter({isActive: true}) - .run() - } -) - export const scalesByTeamId = new RethinkForeignKeyLoaderMaker( 'templateScales', 'teamId', diff --git a/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts b/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts index 1806bdd3efe..1e4f2d32e95 100644 --- a/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts +++ b/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts @@ -16,7 +16,6 @@ export const notifications = new RethinkPrimaryKeyLoaderMaker('Notification') export const organizations = new RethinkPrimaryKeyLoaderMaker('Organization') export const organizationUsers = new RethinkPrimaryKeyLoaderMaker('OrganizationUser') export const templateScales = new RethinkPrimaryKeyLoaderMaker('TemplateScale') -export const retroReflectionGroups = new RethinkPrimaryKeyLoaderMaker('RetroReflectionGroup') export const retroReflections = new RethinkPrimaryKeyLoaderMaker('RetroReflection') export const slackAuths = new RethinkPrimaryKeyLoaderMaker('SlackAuth') export const slackNotifications = new RethinkPrimaryKeyLoaderMaker('SlackNotification') diff --git a/packages/server/graphql/mutations/createReflection.ts b/packages/server/graphql/mutations/createReflection.ts index 7eaa94998f5..67e7420f3c6 100644 --- a/packages/server/graphql/mutations/createReflection.ts +++ b/packages/server/graphql/mutations/createReflection.ts @@ -99,7 +99,6 @@ export default { await Promise.all([ pg.insertInto('RetroReflectionGroup').values(reflectionGroup).execute(), - r.table('RetroReflectionGroup').insert(reflectionGroup).run(), r.table('RetroReflection').insert(reflection).run() ]) diff --git a/packages/server/graphql/mutations/helpers/generateDiscussionPrompt.ts b/packages/server/graphql/mutations/helpers/generateDiscussionPrompt.ts index bb7a245ca2f..2c8e3a094f6 100644 --- a/packages/server/graphql/mutations/helpers/generateDiscussionPrompt.ts +++ b/packages/server/graphql/mutations/helpers/generateDiscussionPrompt.ts @@ -1,4 +1,3 @@ -import getRethink from '../../../database/rethinkDriver' import getKysely from '../../../postgres/getKysely' import OpenAIServerManager from '../../../utils/OpenAIServerManager' import sendToSentry from '../../../utils/sendToSentry' @@ -26,7 +25,6 @@ const generateDiscussionPrompt = async ( dataLoader.get('retroReflectionsByMeetingId').load(meetingId), dataLoader.get('retroReflectionGroupsByMeetingId').load(meetingId) ]) - const r = await getRethink() const pg = getKysely() const manager = new OpenAIServerManager() if (!reflectionGroups.length) { @@ -46,14 +44,11 @@ const generateDiscussionPrompt = async ( ) if (!fullQuestion) return const discussionPromptQuestion = fullQuestion?.slice(0, 2000) - return Promise.all([ - pg - .updateTable('RetroReflectionGroup') - .set({discussionPromptQuestion}) - .where('id', '=', group.id) - .execute(), - r.table('RetroReflectionGroup').get(group.id).update({discussionPromptQuestion}).run() - ]) + return pg + .updateTable('RetroReflectionGroup') + .set({discussionPromptQuestion}) + .where('id', '=', group.id) + .execute() }) ) } diff --git a/packages/server/graphql/mutations/helpers/handleCompletedStage.ts b/packages/server/graphql/mutations/helpers/handleCompletedStage.ts index 8227ec6fe69..94f44d7f401 100644 --- a/packages/server/graphql/mutations/helpers/handleCompletedStage.ts +++ b/packages/server/graphql/mutations/helpers/handleCompletedStage.ts @@ -35,7 +35,6 @@ const handleCompletedRetrospectiveStage = async ( if (stage.phaseType === REFLECT) { const r = await getRethink() const pg = getKysely() - const now = new Date() const [reflectionGroups, reflections] = await Promise.all([ dataLoader.get('retroReflectionGroupsByMeetingId').load(meeting.id), @@ -60,21 +59,11 @@ const handleCompletedRetrospectiveStage = async ( await Promise.all( sortedReflectionGroups.map((group, index) => { group.sortOrder = index - return Promise.all([ - pg - .updateTable('RetroReflectionGroup') - .set({sortOrder: index}) - .where('id', '=', group.id) - .execute(), - r - .table('RetroReflectionGroup') - .get(group.id) - .update({ - sortOrder: index, - updatedAt: now - } as any) - .run() - ]) + return pg + .updateTable('RetroReflectionGroup') + .set({sortOrder: index}) + .where('id', '=', group.id) + .execute() }) ) diff --git a/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts b/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts index 463eeecf935..7ebc7bdcf61 100644 --- a/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts +++ b/packages/server/graphql/mutations/helpers/notifications/SlackNotifier.ts @@ -597,7 +597,7 @@ export const SlackNotifier = { dataLoader.get('teams').loadNonNull(teamId), dataLoader.get('users').loadNonNull(userId), dataLoader.get('newMeetings').load(meetingId), - dataLoader.get('retroReflectionGroups').load(reflectionGroupId), + dataLoader.get('retroReflectionGroups').loadNonNull(reflectionGroupId), r.table('RetroReflection').getAll(reflectionGroupId, {index: 'reflectionGroupId'}).run(), r .table('SlackAuth') diff --git a/packages/server/graphql/mutations/helpers/removeEmptyReflectionGroup.ts b/packages/server/graphql/mutations/helpers/removeEmptyReflectionGroup.ts index b605ffd4bf2..7958457a558 100644 --- a/packages/server/graphql/mutations/helpers/removeEmptyReflectionGroup.ts +++ b/packages/server/graphql/mutations/helpers/removeEmptyReflectionGroup.ts @@ -7,7 +7,6 @@ const removeEmptyReflectionGroup = async ( ) => { const r = await getRethink() const pg = getKysely() - const now = new Date() if (!reflectionGroupId) return false const reflectionCount = await r .table('RetroReflection') @@ -17,18 +16,11 @@ const removeEmptyReflectionGroup = async ( .run() if (reflectionCount > 0) return - return Promise.all([ - pg - .updateTable('RetroReflectionGroup') - .set({isActive: false}) - .where('id', '=', oldReflectionGroupId) - .execute(), - r - .table('RetroReflectionGroup') - .get(oldReflectionGroupId) - .update({isActive: false, updatedAt: now}) - .run() - ]) + return pg + .updateTable('RetroReflectionGroup') + .set({isActive: false}) + .where('id', '=', oldReflectionGroupId) + .execute() } export default removeEmptyReflectionGroup diff --git a/packages/server/graphql/mutations/helpers/removeEmptyReflections.ts b/packages/server/graphql/mutations/helpers/removeEmptyReflections.ts index c3867b2f4da..1745e5d97f8 100644 --- a/packages/server/graphql/mutations/helpers/removeEmptyReflections.ts +++ b/packages/server/graphql/mutations/helpers/removeEmptyReflections.ts @@ -34,14 +34,7 @@ const removeEmptyReflections = async (meeting: Meeting) => { .updateTable('RetroReflectionGroup') .set({isActive: false}) .where('id', 'in', emptyReflectionGroupIds) - .execute(), - r - .table('RetroReflectionGroup') - .getAll(r.args(emptyReflectionGroupIds), {index: 'id'}) - .update({ - isActive: false - }) - .run() + .execute() ]) } return {emptyReflectionGroupIds} diff --git a/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts b/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts index 2c9c282e648..b902b66c3a0 100644 --- a/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts +++ b/packages/server/graphql/mutations/helpers/safeEndRetrospective.ts @@ -172,12 +172,6 @@ const safeEndRetrospective = async ({ .where('meetingId', '=', meetingId) .where('isActive', '=', false) .execute(), - r - .table('RetroReflectionGroup') - .getAll(meetingId, {index: 'meetingId'}) - .filter({isActive: false}) - .delete() - .run(), updateTeamInsights(teamId, dataLoader) ]) // wait for removeEmptyTasks before summarizeRetroMeeting diff --git a/packages/server/graphql/mutations/helpers/safelyCastVote.ts b/packages/server/graphql/mutations/helpers/safelyCastVote.ts index 86d2db1780d..d0e8e030ee9 100644 --- a/packages/server/graphql/mutations/helpers/safelyCastVote.ts +++ b/packages/server/graphql/mutations/helpers/safelyCastVote.ts @@ -5,7 +5,6 @@ import {RValue} from '../../../database/stricterR' import AuthToken from '../../../database/types/AuthToken' import getKysely from '../../../postgres/getKysely' import {getUserId} from '../../../utils/authorization' -import sendToSentry from '../../../utils/sendToSentry' import standardError from '../../../utils/standardError' const safelyCastVote = async ( @@ -40,36 +39,19 @@ const safelyCastVote = async ( return standardError(new Error('No votes remaining'), {userId: viewerId}) } - const [isVoteAddedToGroup, voteAddedResult] = await Promise.all([ - r - .table('RetroReflectionGroup') - .get(reflectionGroupId) - .update((group: RValue) => { - return r.branch( - group('voterIds').count(userId).lt(maxVotesPerGroup), - { - updatedAt: now, - voterIds: group('voterIds').append(userId) - }, - {} - ) - })('replaced') - .eq(1) - .run(), - pg - .updateTable('RetroReflectionGroup') - .set({voterIds: sql`ARRAY_APPEND("voterIds",${userId})`}) - .where('id', '=', reflectionGroupId) - .where( - sql`COALESCE(array_length(array_positions("voterIds", ${userId}),1),0)`, - '<', - maxVotesPerGroup - ) - .executeTakeFirst() - ]) - const isVoteAddedToGroupPG = voteAddedResult.numUpdatedRows === BigInt(1) - if (isVoteAddedToGroupPG !== isVoteAddedToGroup) - sendToSentry(new Error('MISMATCH VOTE CAST LOGIC')) + const voteAddedResult = await pg + .updateTable('RetroReflectionGroup') + .set({voterIds: sql`ARRAY_APPEND("voterIds",${userId})`}) + .where('id', '=', reflectionGroupId) + .where( + sql`COALESCE(array_length(array_positions("voterIds", ${userId}),1),0)`, + '<', + maxVotesPerGroup + ) + .executeTakeFirst() + + const isVoteAddedToGroup = voteAddedResult.numUpdatedRows === BigInt(1) + if (!isVoteAddedToGroup) { await r .table('MeetingMember') diff --git a/packages/server/graphql/mutations/helpers/safelyWithdrawVote.ts b/packages/server/graphql/mutations/helpers/safelyWithdrawVote.ts index 939e0519dcd..b78559ee901 100644 --- a/packages/server/graphql/mutations/helpers/safelyWithdrawVote.ts +++ b/packages/server/graphql/mutations/helpers/safelyWithdrawVote.ts @@ -5,7 +5,6 @@ import {RValue} from '../../../database/stricterR' import AuthToken from '../../../database/types/AuthToken' import getKysely from '../../../postgres/getKysely' import {getUserId} from '../../../utils/authorization' -import sendToSentry from '../../../utils/sendToSentry' import standardError from '../../../utils/standardError' const safelyWithdrawVote = async ( @@ -19,37 +18,18 @@ const safelyWithdrawVote = async ( const pg = getKysely() const now = new Date() const viewerId = getUserId(authToken) - const [isVoteRemovedFromGroup, voteRemovedResult] = await Promise.all([ - r - .table('RetroReflectionGroup') - .get(reflectionGroupId) - .update((group: RValue) => { - return r.branch( - group('voterIds').offsetsOf(userId).count().ge(1), - { - updatedAt: now, - voterIds: group('voterIds').deleteAt(group('voterIds').offsetsOf(userId).nth(0)) - }, - {} - ) - })('replaced') - .eq(1) - .run(), - pg - .updateTable('RetroReflectionGroup') - .set({ - voterIds: sql`array_cat( + const voteRemovedResult = await pg + .updateTable('RetroReflectionGroup') + .set({ + voterIds: sql`array_cat( "voterIds"[1:array_position("voterIds",${userId})-1], "voterIds"[array_position("voterIds",${userId})+1:] )` - }) - .where('id', '=', reflectionGroupId) - .where(sql`${userId}`, '=', sql`ANY("voterIds")`) - .executeTakeFirst() - ]) - const isVoteRemovedFromGroupPG = voteRemovedResult.numUpdatedRows === BigInt(1) - if (isVoteRemovedFromGroup !== isVoteRemovedFromGroupPG) - sendToSentry(new Error('MISMATCH VOTE REMOVED LOGIC')) + }) + .where('id', '=', reflectionGroupId) + .where(sql`${userId}`, '=', sql`ANY("voterIds")`) + .executeTakeFirst() + const isVoteRemovedFromGroup = voteRemovedResult.numUpdatedRows === BigInt(1) if (!isVoteRemovedFromGroup) { return standardError(new Error('Already removed vote'), {userId: viewerId}) } diff --git a/packages/server/graphql/mutations/helpers/updateReflectionLocation/addReflectionToGroup.ts b/packages/server/graphql/mutations/helpers/updateReflectionLocation/addReflectionToGroup.ts index a6d151f0127..5d51532369c 100644 --- a/packages/server/graphql/mutations/helpers/updateReflectionLocation/addReflectionToGroup.ts +++ b/packages/server/graphql/mutations/helpers/updateReflectionLocation/addReflectionToGroup.ts @@ -2,7 +2,6 @@ import dndNoise from 'parabol-client/utils/dndNoise' import getGroupSmartTitle from 'parabol-client/utils/smartGroup/getGroupSmartTitle' import getRethink from '../../../../database/rethinkDriver' import Reflection from '../../../../database/types/Reflection' -import ReflectionGroup from '../../../../database/types/ReflectionGroup' import getKysely from '../../../../postgres/getKysely' import {GQLContext} from './../../../graphql' import updateSmartGroupTitle from './updateSmartGroupTitle' @@ -19,14 +18,13 @@ const addReflectionToGroup = async ( const reflection = await dataLoader.get('retroReflections').load(reflectionId) if (!reflection) throw new Error('Reflection not found') const {reflectionGroupId: oldReflectionGroupId, meetingId: reflectionMeetingId} = reflection - const {reflectionGroup, oldReflectionGroup} = await r({ - reflectionGroup: r - .table('RetroReflectionGroup') - .get(reflectionGroupId) as unknown as ReflectionGroup, - oldReflectionGroup: r - .table('RetroReflectionGroup') - .get(oldReflectionGroupId) as unknown as ReflectionGroup - }).run() + const [reflectionGroup, oldReflectionGroup] = await Promise.all([ + dataLoader.get('retroReflectionGroups').loadNonNull(reflectionGroupId), + dataLoader.get('retroReflectionGroups').loadNonNull(oldReflectionGroupId) + ]) + dataLoader.get('retroReflectionGroups').clear(reflectionGroupId) + dataLoader.get('retroReflectionGroups').clear(oldReflectionGroupId) + if (!reflectionGroup || !reflectionGroup.isActive) { throw new Error('Reflection group not found') } @@ -79,22 +77,11 @@ const addReflectionToGroup = async ( const newGroupHasSmartTitle = reflectionGroup.title === reflectionGroup.smartTitle if (oldGroupHasSingleReflectionCustomTitle && newGroupHasSmartTitle) { // Edge case of dragging a single card with a custom group name on a group with smart name - await Promise.all([ - pg - .updateTable('RetroReflectionGroup') - .set({title: oldReflectionGroup.title, smartTitle: nextTitle}) - .where('id', '=', reflectionGroupId) - .execute(), - r - .table('RetroReflectionGroup') - .get(reflectionGroupId) - .update({ - title: oldReflectionGroup.title, - smartTitle: nextTitle, - updatedAt: now - }) - .run() - ]) + await pg + .updateTable('RetroReflectionGroup') + .set({title: oldReflectionGroup.title, smartTitle: nextTitle}) + .where('id', '=', reflectionGroupId) + .execute() } else { await updateSmartGroupTitle(reflectionGroupId, nextTitle) } @@ -103,21 +90,11 @@ const addReflectionToGroup = async ( const oldTitle = getGroupSmartTitle(oldReflections) await updateSmartGroupTitle(oldReflectionGroupId, oldTitle) } else { - await Promise.all([ - pg - .updateTable('RetroReflectionGroup') - .set({isActive: false}) - .where('id', '=', oldReflectionGroupId) - .execute(), - r - .table('RetroReflectionGroup') - .get(oldReflectionGroupId) - .update({ - isActive: false, - updatedAt: now - }) - .run() - ]) + await pg + .updateTable('RetroReflectionGroup') + .set({isActive: false}) + .where('id', '=', oldReflectionGroupId) + .execute() } } return reflectionGroupId diff --git a/packages/server/graphql/mutations/helpers/updateReflectionLocation/removeReflectionFromGroup.ts b/packages/server/graphql/mutations/helpers/updateReflectionLocation/removeReflectionFromGroup.ts index 66cbb18c44f..c7166d817dc 100644 --- a/packages/server/graphql/mutations/helpers/updateReflectionLocation/removeReflectionFromGroup.ts +++ b/packages/server/graphql/mutations/helpers/updateReflectionLocation/removeReflectionFromGroup.ts @@ -14,17 +14,16 @@ const removeReflectionFromGroup = async (reflectionId: string, {dataLoader}: GQL const reflection = await dataLoader.get('retroReflections').load(reflectionId) if (!reflection) throw new Error('Reflection not found') const {reflectionGroupId: oldReflectionGroupId, meetingId, promptId} = reflection - const [oldReflectionGroup, reflectionGroupsInColumn, meeting] = await Promise.all([ - dataLoader.get('retroReflectionGroups').load(oldReflectionGroupId), - r - .table('RetroReflectionGroup') - .getAll(meetingId, {index: 'meetingId'}) - .filter({isActive: true, promptId}) - .orderBy('sortOrder') - .run(), + const [meetingReflectionGroups, meeting] = await Promise.all([ + dataLoader.get('retroReflectionGroupsByMeetingId').load(meetingId), dataLoader.get('newMeetings').load(meetingId) ]) - + dataLoader.get('retroReflectionGroupsByMeetingId').clear(meetingId) + dataLoader.get('retroReflectionGroups').clearAll() + const oldReflectionGroup = meetingReflectionGroups.find((g) => g.id === oldReflectionGroupId)! + const reflectionGroupsInColumn = meetingReflectionGroups + .filter((g) => g.promptId === promptId) + .sort((a, b) => (a.sortOrder < b.sortOrder ? -1 : 1)) let newSortOrder = 1e6 const oldReflectionGroupIdx = reflectionGroupsInColumn.findIndex( (group) => group.id === oldReflectionGroup.id @@ -47,7 +46,6 @@ const removeReflectionFromGroup = async (reflectionId: string, {dataLoader}: GQL const {id: reflectionGroupId} = reflectionGroup await Promise.all([ pg.insertInto('RetroReflectionGroup').values(reflectionGroup).execute(), - r.table('RetroReflectionGroup').insert(reflectionGroup).run(), r .table('RetroReflection') .get(reflectionId) @@ -77,21 +75,11 @@ const removeReflectionFromGroup = async (reflectionId: string, {dataLoader}: GQL const oldTitle = getGroupSmartTitle(oldReflections) await updateSmartGroupTitle(oldReflectionGroupId, oldTitle) } else { - await Promise.all([ - pg - .updateTable('RetroReflectionGroup') - .set({isActive: false}) - .where('id', '=', oldReflectionGroupId) - .execute(), - r - .table('RetroReflectionGroup') - .get(oldReflectionGroupId) - .update({ - isActive: false, - updatedAt: now - }) - .run() - ]) + await pg + .updateTable('RetroReflectionGroup') + .set({isActive: false}) + .where('id', '=', oldReflectionGroupId) + .execute() } return reflectionGroupId } diff --git a/packages/server/graphql/mutations/helpers/updateReflectionLocation/updateSmartGroupTitle.ts b/packages/server/graphql/mutations/helpers/updateReflectionLocation/updateSmartGroupTitle.ts index da34c3ab2fd..90aab3da6cc 100644 --- a/packages/server/graphql/mutations/helpers/updateReflectionLocation/updateSmartGroupTitle.ts +++ b/packages/server/graphql/mutations/helpers/updateReflectionLocation/updateSmartGroupTitle.ts @@ -1,31 +1,16 @@ import {sql} from 'kysely' -import getRethink from '../../../../database/rethinkDriver' -import {RValue} from '../../../../database/stricterR' import getKysely from '../../../../postgres/getKysely' const updateSmartGroupTitle = async (reflectionGroupId: string, smartTitle: string) => { - const r = await getRethink() const pg = getKysely() - const now = new Date() - await Promise.all([ - pg - .updateTable('RetroReflectionGroup') - .set({ - smartTitle, - title: sql`CASE WHEN "smartTitle" = "title" THEN ${smartTitle} ELSE "title" END` - }) - .where('id', '=', reflectionGroupId) - .execute(), - r - .table('RetroReflectionGroup') - .get(reflectionGroupId) - .update((g: RValue) => ({ - smartTitle, - title: r.branch(g('smartTitle').eq(g('title')), smartTitle, g('title')), - updatedAt: now - })) - .run() - ]) + await pg + .updateTable('RetroReflectionGroup') + .set({ + smartTitle, + title: sql`CASE WHEN "smartTitle" = "title" THEN ${smartTitle} ELSE "title" END` + }) + .where('id', '=', reflectionGroupId) + .execute() } export default updateSmartGroupTitle diff --git a/packages/server/graphql/mutations/resetRetroMeetingToGroupStage.ts b/packages/server/graphql/mutations/resetRetroMeetingToGroupStage.ts index 721182c7242..acb1a07e001 100644 --- a/packages/server/graphql/mutations/resetRetroMeetingToGroupStage.ts +++ b/packages/server/graphql/mutations/resetRetroMeetingToGroupStage.ts @@ -115,11 +115,6 @@ const resetRetroMeetingToGroupStage = { .set({voterIds: [], summary: null, discussionPromptQuestion: null}) .where('id', 'in', reflectionGroupIds) .execute(), - r - .table('RetroReflectionGroup') - .getAll(r.args(reflectionGroupIds)) - .update({voterIds: [], summary: null, discussionPromptQuestion: null}) - .run(), r.table('NewMeeting').get(meetingId).update({phases: newPhases}).run(), (r.table('MeetingMember').getAll(meetingId, {index: 'meetingId'}) as any) .update({votesRemaining: meeting.totalVotes}) diff --git a/packages/server/graphql/mutations/updateReflectionGroupTitle.ts b/packages/server/graphql/mutations/updateReflectionGroupTitle.ts index 24e612d8615..41a3ca4978c 100644 --- a/packages/server/graphql/mutations/updateReflectionGroupTitle.ts +++ b/packages/server/graphql/mutations/updateReflectionGroupTitle.ts @@ -2,7 +2,6 @@ import {GraphQLID, GraphQLNonNull, GraphQLString} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import isPhaseComplete from 'parabol-client/utils/meetings/isPhaseComplete' import stringSimilarity from 'string-similarity' -import getRethink from '../../database/rethinkDriver' import getKysely from '../../postgres/getKysely' import {analytics} from '../../utils/analytics/analytics' import {getUserId, isTeamMember} from '../../utils/authorization' @@ -32,14 +31,14 @@ export default { {reflectionGroupId, title}: UpdateReflectionGroupTitleMutationVariables, {authToken, dataLoader, socketId: mutatorId}: GQLContext ) { - const r = await getRethink() const pg = getKysely() const operationId = dataLoader.share() const subOptions = {operationId, mutatorId} // AUTH const viewerId = getUserId(authToken) - const reflectionGroup = await r.table('RetroReflectionGroup').get(reflectionGroupId).run() + const reflectionGroup = await dataLoader.get('retroReflectionGroups').load(reflectionGroupId) + if (!reflectionGroup) { return standardError(new Error('Reflection group not found'), {userId: viewerId}) } @@ -70,30 +69,19 @@ export default { return {error: {message: 'Title is too long'}} } - const allTitles = await r - .table('RetroReflectionGroup') - .getAll(meetingId, {index: 'meetingId'}) - .filter({isActive: true})('title') - .run() + const allGroups = await dataLoader.get('retroReflectionGroupsByMeetingId').load(meetingId) + const allTitles = allGroups.map((g) => g.title) if (allTitles.includes(normalizedTitle)) { return standardError(new Error('Group titles must be unique'), {userId: viewerId}) } // RESOLUTION - await Promise.all([ - r - .table('RetroReflectionGroup') - .get(reflectionGroupId) - .update({ - title: normalizedTitle - }) - .run(), - pg - .updateTable('RetroReflectionGroup') - .set({title: normalizedTitle}) - .where('id', '=', reflectionGroupId) - .execute() - ]) + dataLoader.get('retroReflectionGroups').clear(reflectionGroupId) + await pg + .updateTable('RetroReflectionGroup') + .set({title: normalizedTitle}) + .where('id', '=', reflectionGroupId) + .execute() if (smartTitle && smartTitle === oldTitle) { // let's see how smart those smart titles really are. A high similarity means very helpful. Not calling this mutation means perfect! diff --git a/packages/server/graphql/mutations/voteForReflectionGroup.ts b/packages/server/graphql/mutations/voteForReflectionGroup.ts index fa2d0882c1a..fe193c618c9 100644 --- a/packages/server/graphql/mutations/voteForReflectionGroup.ts +++ b/packages/server/graphql/mutations/voteForReflectionGroup.ts @@ -35,7 +35,7 @@ export default { // AUTH const viewerId = getUserId(authToken) - const reflectionGroup = await r.table('RetroReflectionGroup').get(reflectionGroupId).run() + const reflectionGroup = await dataLoader.get('retroReflectionGroups').load(reflectionGroupId) if (!reflectionGroup || !reflectionGroup.isActive) { return standardError(new Error('Reflection group not found'), { userId: viewerId, @@ -66,6 +66,7 @@ export default { } // RESOLUTION + dataLoader.get('retroReflectionGroups').clear(reflectionGroupId) if (isUnvote) { const votingError = await safelyWithdrawVote( authToken, diff --git a/packages/server/graphql/private/mutations/backupOrganization.ts b/packages/server/graphql/private/mutations/backupOrganization.ts index 5938ccbfbd4..f41038a198d 100644 --- a/packages/server/graphql/private/mutations/backupOrganization.ts +++ b/packages/server/graphql/private/mutations/backupOrganization.ts @@ -280,23 +280,6 @@ const backupOrganization: MutationResolvers['backupOrganization'] = async (_sour ) .coerceTo('array') .do((items: RValue) => r.db(DESTINATION).table('RetroReflection').insert(items)), - retroReflectionGroup: ( - r.table('RetroReflectionGroup').getAll(r.args(meetingIds), {index: 'meetingId'}) as any - ) - .coerceTo('array') - .do((items: RValue) => r.db(DESTINATION).table('RetroReflectionGroup').insert(items)), - // really hard things to clone - reflectionGroupComments: r - .table('RetroReflectionGroup') - .getAll(r.args(meetingIds), {index: 'meetingId'})('id') - .coerceTo('array') - .do((discussionIds: RValue) => { - return ( - r.table('Comment').getAll(r.args(discussionIds), {index: 'discussionId'}) as any - ) - .coerceTo('array') - .do((items: RValue) => r.db(DESTINATION).table('Comment').insert(items)) - }), agendaItemComments: r .table('AgendaItem') .getAll(r.args(meetingIds), {index: 'meetingId'})('id') diff --git a/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts b/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts index 54e4ea2713b..932c9bccf40 100644 --- a/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts +++ b/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts @@ -35,7 +35,7 @@ const checkRethinkPgEquality: MutationResolvers['checkRethinkPgEquality'] = asyn const rowCountResult = await checkRowCount(tableName) const rethinkQuery = (updatedAt: Date, id: string | number) => { return r - .table('RetroReflectionGroup') + .table('RetroReflectionGroup' as any) .between([updatedAt, id], [r.maxval, r.maxval], { index: 'updatedAtId', leftBound: 'open', diff --git a/packages/server/graphql/public/types/RetroReflectionGroup.ts b/packages/server/graphql/public/types/RetroReflectionGroup.ts new file mode 100644 index 00000000000..404834dcc57 --- /dev/null +++ b/packages/server/graphql/public/types/RetroReflectionGroup.ts @@ -0,0 +1,9 @@ +import {Selectable} from 'kysely' +import {RetroReflectionGroup as TRetroReflectionGroup} from '../../../postgres/pg' +import {RetroReflectionGroupResolvers} from '../resolverTypes' + +export interface RetroReflectionGroupSource extends Selectable {} + +const RetroReflectionGroup: RetroReflectionGroupResolvers = {} + +export default RetroReflectionGroup diff --git a/packages/server/graphql/public/types/RetrospectiveMeeting.ts b/packages/server/graphql/public/types/RetrospectiveMeeting.ts index a4ad2df6f03..1501e3805e7 100644 --- a/packages/server/graphql/public/types/RetrospectiveMeeting.ts +++ b/packages/server/graphql/public/types/RetrospectiveMeeting.ts @@ -1,5 +1,4 @@ import toTeamMemberId from '../../../../client/utils/relay/toTeamMemberId' -import ReflectionGroupType from '../../../database/types/ReflectionGroup' import RetroMeetingMember from '../../../database/types/RetroMeetingMember' import {getUserId} from '../../../utils/authorization' import filterTasksByMeeting from '../../../utils/filterTasksByMeeting' @@ -20,7 +19,7 @@ const RetrospectiveMeeting: RetrospectiveMeetingResolvers = { reflectionCount: ({reflectionCount}) => reflectionCount || 0, reflectionGroup: async ({id: meetingId}, {reflectionGroupId}, {dataLoader}) => { const reflectionGroup = await dataLoader.get('retroReflectionGroups').load(reflectionGroupId) - if (reflectionGroup.meetingId !== meetingId) return null + if (reflectionGroup?.meetingId !== meetingId) return null return reflectionGroup }, reflectionGroups: async ({id: meetingId}, {sortBy}, {dataLoader}) => { @@ -28,9 +27,7 @@ const RetrospectiveMeeting: RetrospectiveMeetingResolvers = { .get('retroReflectionGroupsByMeetingId') .load(meetingId) if (sortBy === 'voteCount') { - reflectionGroups.sort((a: ReflectionGroupType, b: ReflectionGroupType) => - a.voterIds.length < b.voterIds.length ? 1 : -1 - ) + reflectionGroups.sort((a, b) => (a.voterIds.length < b.voterIds.length ? 1 : -1)) return reflectionGroups } else if (sortBy === 'stageOrder') { const meeting = await dataLoader.get('newMeetings').load(meetingId) @@ -40,18 +37,16 @@ const RetrospectiveMeeting: RetrospectiveMeetingResolvers = { const {stages} = discussPhase // for early terminations the stages may not exist const sortLookup = {} as {[reflectionGroupId: string]: number} - reflectionGroups.forEach((group: ReflectionGroupType) => { + reflectionGroups.forEach((group) => { const idx = stages.findIndex((stage) => stage.reflectionGroupId === group.id) sortLookup[group.id] = idx }) - reflectionGroups.sort((a: ReflectionGroupType, b: ReflectionGroupType) => { + reflectionGroups.sort((a, b) => { return sortLookup[a.id]! < sortLookup[b.id]! ? -1 : 1 }) return reflectionGroups } - reflectionGroups.sort((a: ReflectionGroupType, b: ReflectionGroupType) => - a.sortOrder < b.sortOrder ? -1 : 1 - ) + reflectionGroups.sort((a, b) => (a.sortOrder < b.sortOrder ? -1 : 1)) return reflectionGroups }, taskCount: ({taskCount}) => taskCount || 0,