Skip to content

Commit

Permalink
chore(rethinkdb): MeetingSettings: Phase 1 (#10088)
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Krick <matt.krick@gmail.com>
  • Loading branch information
mattkrick authored Aug 12, 2024
1 parent bf1851e commit 40d8c8c
Show file tree
Hide file tree
Showing 16 changed files with 252 additions and 51 deletions.
23 changes: 0 additions & 23 deletions packages/server/database/rethinkDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ import SlackAuth from '../database/types/SlackAuth'
import SlackNotification from '../database/types/SlackNotification'
import TeamInvitation from '../database/types/TeamInvitation'
import {AnyMeeting, AnyMeetingSettings, AnyMeetingTeamMember} from '../postgres/types/Meeting'
import {ScheduledJobUnion} from '../types/custom'
import getRethinkConfig from './getRethinkConfig'
import {R} from './stricterR'
import AgendaItem from './types/AgendaItem'
import AtlassianAuth from './types/AtlassianAuth'
import Comment from './types/Comment'
import FailedAuthRequest from './types/FailedAuthRequest'
import MassInvitation from './types/MassInvitation'
import NotificationKickedOut from './types/NotificationKickedOut'
import NotificationMeetingStageTimeLimitEnd from './types/NotificationMeetingStageTimeLimitEnd'
Expand All @@ -31,10 +28,6 @@ export type RethinkSchema = {
type: AgendaItem
index: 'teamId' | 'meetingId'
}
AtlassianAuth: {
type: AtlassianAuth
index: 'atlassianUserId' | 'userId' | 'teamId'
}
Comment: {
type: Comment
index: 'discussionId'
Expand All @@ -43,18 +36,6 @@ export type RethinkSchema = {
type: RetrospectivePrompt
index: 'teamId' | 'templateId'
}
EmailVerification: {
type: any
index: 'email' | 'token'
}
FailedAuthRequest: {
type: FailedAuthRequest
index: 'email' | 'ip'
}
GQLRequest: {
type: any
index: 'id'
}
MassInvitation: {
type: MassInvitation
index: 'teamMemberId'
Expand Down Expand Up @@ -102,10 +83,6 @@ export type RethinkSchema = {
type: PushInvitation
index: 'userId'
}
ScheduledJob: {
type: ScheduledJobUnion
index: 'runAt' | 'type'
}
SlackAuth: {
type: SlackAuth
index: 'teamId' | 'userId'
Expand Down
35 changes: 30 additions & 5 deletions packages/server/dataloader/customLoaderMakers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import DataLoader from 'dataloader'
import tracer from 'dd-trace'
import {Selectable, SqlBool, sql} from 'kysely'
import {PARABOL_AI_USER_ID} from '../../client/utils/constants'
import getRethink, {RethinkSchema} from '../database/rethinkDriver'
import getRethink from '../database/rethinkDriver'
import {RDatum} from '../database/stricterR'
import MeetingSettingsTeamPrompt from '../database/types/MeetingSettingsTeamPrompt'
import MeetingTemplate from '../database/types/MeetingTemplate'
Expand All @@ -26,8 +26,8 @@ import getLatestTaskEstimates from '../postgres/queries/getLatestTaskEstimates'
import getMeetingTaskEstimates, {
MeetingTaskEstimatesResult
} from '../postgres/queries/getMeetingTaskEstimates'
import {selectTeams} from '../postgres/select'
import {OrganizationUser, Team} from '../postgres/types'
import {selectMeetingSettings, selectTeams} from '../postgres/select'
import {MeetingSettings, OrganizationUser, Team} from '../postgres/types'
import {AnyMeeting, MeetingTypeEnum} from '../postgres/types/Meeting'
import {Logger} from '../utils/Logger'
import getRedis from '../utils/getRedis'
Expand Down Expand Up @@ -288,7 +288,7 @@ export const githubDimensionFieldMaps = (parent: RootDataLoader) => {

export const meetingSettingsByType = (parent: RootDataLoader, dependsOn: RegisterDependsOn) => {
dependsOn('meetingSettings')
return new DataLoader<MeetingSettingsKey, RethinkSchema['MeetingSettings']['type'], string>(
return new DataLoader<MeetingSettingsKey, MeetingSettings, string>(
async (keys) => {
const r = await getRethink()
const types = {} as Record<MeetingTypeEnum, string[]>
Expand All @@ -313,7 +313,7 @@ export const meetingSettingsByType = (parent: RootDataLoader, dependsOn: Registe
const {teamId, meetingType} = key
// until we decide the final shape of the team prompt settings, let's return a temporary hardcoded value
if (meetingType === 'teamPrompt') {
return new MeetingSettingsTeamPrompt({teamId})
return new MeetingSettingsTeamPrompt({teamId}) as any
}
return docs.find((doc) => doc.teamId === teamId && doc.meetingType === meetingType)!
})
Expand All @@ -325,6 +325,31 @@ export const meetingSettingsByType = (parent: RootDataLoader, dependsOn: Registe
)
}

export const _PGmeetingSettingsByType = (parent: RootDataLoader, dependsOn: RegisterDependsOn) => {
dependsOn('meetingSettings')
return new DataLoader<MeetingSettingsKey, MeetingSettings, string>(
async (keys) => {
const res = await selectMeetingSettings()
.where(({eb, refTuple, tuple}) =>
eb(
refTuple('teamId', 'meetingType'),
'in',
keys.map((key) => tuple(key.teamId, key.meetingType))
)
)
.execute()
return keys.map(
(key) =>
res.find((doc) => doc.teamId === key.teamId && doc.meetingType === key.meetingType)!
)
},
{
...parent.dataLoaderOptions,
cacheKeyFn: (key) => `${key.teamId}:${key.meetingType}`
}
)
}

export const organizationApprovedDomainsByOrgId = (parent: RootDataLoader) => {
return new DataLoader<string, string[], string>(
async (orgIds) => {
Expand Down
5 changes: 5 additions & 0 deletions packages/server/dataloader/primaryKeyLoaderMakers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import getMeetingTemplatesByIds from '../postgres/queries/getMeetingTemplatesByI
import getTemplateRefsByIds from '../postgres/queries/getTemplateRefsByIds'
import {getUsersByIds} from '../postgres/queries/getUsersByIds'
import {
selectMeetingSettings,
selectOrganizations,
selectRetroReflections,
selectSuggestedAction,
Expand Down Expand Up @@ -85,3 +86,7 @@ export const templateDimensions = primaryKeyLoaderMaker((ids: readonly string[])
export const suggestedActions = primaryKeyLoaderMaker((ids: readonly string[]) => {
return selectSuggestedAction().where('id', 'in', ids).execute()
})

export const _PGmeetingSettings = primaryKeyLoaderMaker((ids: readonly string[]) => {
return selectMeetingSettings().where('id', 'in', ids).execute()
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import RethinkPrimaryKeyLoaderMaker from './RethinkPrimaryKeyLoaderMaker'
* all rethink dataloader types which also must exist in {@link rethinkDriver/RethinkSchema}
*/
export const agendaItems = new RethinkPrimaryKeyLoaderMaker('AgendaItem')
export const atlassianAuths = new RethinkPrimaryKeyLoaderMaker('AtlassianAuth')
export const comments = new RethinkPrimaryKeyLoaderMaker('Comment')
export const reflectPrompts = new RethinkPrimaryKeyLoaderMaker('ReflectPrompt')
export const massInvitations = new RethinkPrimaryKeyLoaderMaker('MassInvitation')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default async function createTeamAndLeader(
const organization = await dataLoader.get('organizations').loadNonNull(orgId)
const {tier, trialStartDate} = organization
const verifiedTeam = new Team({...newTeam, createdBy: userId, tier, trialStartDate})

const meetingSettings = [
new MeetingSettingsRetrospective({teamId}),
new MeetingSettingsAction({teamId}),
Expand Down Expand Up @@ -77,6 +78,11 @@ export default async function createTeamAndLeader(
.values(suggestedAction)
.onConflict((oc) => oc.columns(['userId', 'type']).doNothing())
)
.with('MeetingSettingsInsert', (qc) =>
qc
.insertInto('MeetingSettings')
.values(meetingSettings.map((s) => ({...s, jiraSearchQueries: null})))
)
.insertInto('TimelineEvent')
.values(timelineEvent)
.execute(),
Expand Down
15 changes: 9 additions & 6 deletions packages/server/graphql/mutations/removePokerTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,9 @@ const removePokerTemplate = {
const {teamId} = template
const [templates, settings] = await Promise.all([
dataLoader.get('meetingTemplatesByType').load({meetingType: 'poker', teamId}),
r
.table('MeetingSettings')
.getAll(teamId, {index: 'teamId'})
.filter({meetingType: 'poker'})
.nth(0)
.run() as unknown as MeetingSettingsPoker
dataLoader
.get('meetingSettingsByType')
.load({meetingType: 'poker', teamId}) as any as MeetingSettingsPoker
])

// RESOLUTION
Expand All @@ -66,13 +63,19 @@ const removePokerTemplate = {
if (settings.selectedTemplateId === templateId) {
const nextTemplate = templates.find((template) => template.id !== templateId)
const nextTemplateId = nextTemplate?.id ?? SprintPokerDefaults.DEFAULT_TEMPLATE_ID
await getKysely()
.updateTable('MeetingSettings')
.set({selectedTemplateId: nextTemplateId})
.where('id', '=', settingsId)
.execute()
await r
.table('MeetingSettings')
.get(settingsId)
.update({
selectedTemplateId: nextTemplateId
})
.run()
dataLoader.clearAll('meetingSettings')
}

const data = {templateId, settingsId}
Expand Down
16 changes: 10 additions & 6 deletions packages/server/graphql/mutations/removeReflectTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {GraphQLID, GraphQLNonNull} from 'graphql'
import {SubscriptionChannel} from 'parabol-client/types/constEnums'
import getRethink from '../../database/rethinkDriver'
import MeetingSettingsRetrospective from '../../database/types/MeetingSettingsRetrospective'
import getKysely from '../../postgres/getKysely'
import removeMeetingTemplate from '../../postgres/queries/removeMeetingTemplate'
import {getUserId, isTeamMember} from '../../utils/authorization'
import publish from '../../utils/publish'
Expand Down Expand Up @@ -41,12 +42,9 @@ const removeReflectTemplate = {
const {teamId} = template
const [templates, settings] = await Promise.all([
dataLoader.get('meetingTemplatesByType').load({meetingType: 'retrospective', teamId}),
r
.table('MeetingSettings')
.getAll(teamId, {index: 'teamId'})
.filter({meetingType: 'retrospective'})
.nth(0)
.run() as unknown as MeetingSettingsRetrospective
dataLoader
.get('meetingSettingsByType')
.load({meetingType: 'retrospective', teamId}) as any as MeetingSettingsRetrospective
])

// RESOLUTION
Expand All @@ -69,13 +67,19 @@ const removeReflectTemplate = {
if (settings.selectedTemplateId === templateId) {
const nextTemplate = templates.find((template) => template.id !== templateId)
const nextTemplateId = nextTemplate?.id ?? 'workingStuckTemplate'
await getKysely()
.updateTable('MeetingSettings')
.set({selectedTemplateId: nextTemplateId})
.where('id', '=', settingsId)
.execute()
await r
.table('MeetingSettings')
.get(settingsId)
.update({
selectedTemplateId: nextTemplateId
})
.run()
dataLoader.clearAll('meetingSettings')
}

const data = {templateId, settingsId}
Expand Down
9 changes: 8 additions & 1 deletion packages/server/graphql/mutations/selectTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {GraphQLID, GraphQLNonNull} from 'graphql'
import {SubscriptionChannel} from 'parabol-client/types/constEnums'
import getRethink from '../../database/rethinkDriver'
import MeetingTemplate from '../../database/types/MeetingTemplate'
import getKysely from '../../postgres/getKysely'
import {Logger} from '../../utils/Logger'
import {getUserId, isTeamMember} from '../../utils/authorization'
import publish from '../../utils/publish'
Expand Down Expand Up @@ -67,7 +68,13 @@ const selectTemplate = {
)('changes')(0)('old_val')('id')
.default(null)
.run()

await getKysely()
.updateTable('MeetingSettings')
.set({selectedTemplateId})
.where('teamId', '=', teamId)
.where('meetingType', '=', template.type)
.returning('id')
.executeTakeFirst()
// No need to check if a non-null 'meetingSettingsId' was returned - the Activity Library client
// will always attempt to update the template, even if it's already selected, and we don't need
// to return a 'meetingSettingsId' if no updates took place.
Expand Down
10 changes: 10 additions & 0 deletions packages/server/graphql/mutations/updateRetroMaxVotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import mode from 'parabol-client/utils/mode'
import getRethink from '../../database/rethinkDriver'
import {RValue} from '../../database/stricterR'
import MeetingRetrospective from '../../database/types/MeetingRetrospective'
import getKysely from '../../postgres/getKysely'
import {getUserId, isTeamMember} from '../../utils/authorization'
import publish from '../../utils/publish'
import standardError from '../../utils/standardError'
Expand Down Expand Up @@ -136,6 +137,15 @@ const updateRetroMaxVotes = {

// RESOLUTION
await Promise.all([
getKysely()
.updateTable('MeetingSettings')
.set({
totalVotes,
maxVotesPerGroup
})
.where('teamId', '=', teamId)
.where('meetingType', '=', 'retrospective')
.execute(),
r
.table('MeetingSettings')
.getAll(teamId, {index: 'teamId'})
Expand Down
49 changes: 43 additions & 6 deletions packages/server/graphql/public/mutations/setMeetingSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ import {SubscriptionChannel} from 'parabol-client/types/constEnums'
import {isNotNull} from 'parabol-client/utils/predicates'
import getRethink from '../../../database/rethinkDriver'
import {RValue} from '../../../database/stricterR'
import {analytics, MeetingSettings} from '../../../utils/analytics/analytics'
import getKysely from '../../../postgres/getKysely'
import {MeetingSettings} from '../../../postgres/types'
import {
analytics,
MeetingSettings as MeetingSettingsAnalytics
} from '../../../utils/analytics/analytics'
import {getUserId} from '../../../utils/authorization'
import publish from '../../../utils/publish'
import standardError from '../../../utils/standardError'
import {MutationResolvers} from '../resolverTypes'
import {MutationResolvers, NewMeetingPhaseTypeEnum} from '../resolverTypes'

const setMeetingSettings: MutationResolvers['setMeetingSettings'] = async (
_source,
Expand All @@ -19,13 +24,13 @@ const setMeetingSettings: MutationResolvers['setMeetingSettings'] = async (

// AUTH
const viewerId = getUserId(authToken)
const settings = await r.table('MeetingSettings').get(settingsId).run()
const settings = (await dataLoader.get('meetingSettings').load(settingsId)) as MeetingSettings
if (!settings) {
return standardError(new Error('Settings not found'), {userId: viewerId})
}

// RESOLUTION
const {teamId, meetingType} = settings
const {teamId, meetingType, phaseTypes} = settings
const [team, viewer] = await Promise.all([
dataLoader.get('teams').loadNonNull(teamId),
dataLoader.get('users').loadNonNull(viewerId)
Expand All @@ -34,7 +39,27 @@ const setMeetingSettings: MutationResolvers['setMeetingSettings'] = async (
const {featureFlags} = organization
const hasTranscriptFlag = featureFlags?.includes('zoomTranscription')

const meetingSettings = {} as MeetingSettings
const meetingSettings = {} as MeetingSettingsAnalytics
const firstPhases: NewMeetingPhaseTypeEnum[] = []
if (checkinEnabled || (checkinEnabled !== false && phaseTypes.includes('checkin'))) {
firstPhases.push('checkin')
}
if (teamHealthEnabled || (teamHealthEnabled !== false && phaseTypes.includes('TEAM_HEALTH'))) {
firstPhases.push('TEAM_HEALTH')
}
const nextSettings = {
phaseTypes: [
...firstPhases,
...phaseTypes.filter((phase) => phase !== 'checkin' && phase !== 'TEAM_HEALTH')
],
disableAnonymity: isNotNull(disableAnonymity) ? disableAnonymity : settings.disableAnonymity,
videoMeetingURL: hasTranscriptFlag
? isNotNull(videoMeetingURL)
? videoMeetingURL
: settings.videoMeetingURL
: null
}

await r
.table('MeetingSettings')
.get(settingsId)
Expand Down Expand Up @@ -74,8 +99,20 @@ const setMeetingSettings: MutationResolvers['setMeetingSettings'] = async (
})
.run()

await getKysely()
.updateTable('MeetingSettings')
.set(nextSettings)
.where('id', '=', settings.id)
.execute()
dataLoader.clearAll('meetingSettings')

const data = {settingsId}
analytics.meetingSettingsChanged(viewer, teamId, meetingType, meetingSettings)
analytics.meetingSettingsChanged(viewer, teamId, meetingType, {
disableAnonymity: nextSettings.disableAnonymity,
videoMeetingURL: nextSettings.videoMeetingURL,
hasIcebreaker: nextSettings.phaseTypes.includes('checkin'),
hasTeamHealth: nextSettings.phaseTypes.includes('TEAM_HEALTH')
})
publish(SubscriptionChannel.TEAM, teamId, 'SetMeetingSettingsPayload', data, subOptions)
return data
}
Expand Down
Loading

0 comments on commit 40d8c8c

Please sign in to comment.