Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(rethinkdb): RetroReflection: Phase 1 #9820

Merged
merged 13 commits into from
Jun 25, 2024
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"husky": "^7.0.4",
"jscodeshift": "^0.14.0",
"kysely": "^0.27.3",
"kysely-codegen": "^0.11.0",
"kysely-codegen": "^0.15.0",
"lerna": "^6.4.1",
"mini-css-extract-plugin": "^2.7.2",
"minimist": "^1.2.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/embedder/indexing/retrospectiveDiscussionTopic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {ISO6391} from '../iso6393To1'

const IGNORE_COMMENT_USER_IDS = ['parabolAIUser']
const MAX_TEXT_LENGTH = 10000
async function getPreferredNameByUserId(userId: string, dataLoader: DataLoaderInstance) {
async function getPreferredNameByUserId(userId: string | null, dataLoader: DataLoaderInstance) {
if (!userId) return 'Unknown'
const user = await dataLoader.get('users').load(userId)
return !user ? 'Unknown' : user.preferredName
Expand Down
17 changes: 15 additions & 2 deletions packages/server/database/types/Reflection.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import {sql} from 'kysely'
import extractTextFromDraftString from 'parabol-client/utils/draftjs/extractTextFromDraftString'
import generateUID from '../../generateUID'
import GoogleAnalyzedEntity from './GoogleAnalyzedEntity'
import Reactji from './Reactji'

export const toGoogleAnalyzedEntityPG = (entities: GoogleAnalyzedEntity[]) =>
sql<
string[]
>`(select coalesce(array_agg((name, salience, lemma)::"GoogleAnalyzedEntity"), '{}') from json_populate_recordset(null::"GoogleAnalyzedEntity", ${JSON.stringify(entities)}))`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have to be complex here to handle entities like Google, Inc. since that name is stored in a composite type & it has a comma in it.


export interface ReflectionInput {
id?: string
createdAt?: Date
Expand All @@ -21,10 +27,9 @@ export interface ReflectionInput {

export default class Reflection {
id: string
autoReflectionGroupId?: string
createdAt: Date
// userId of the creator
creatorId: string
creatorId: string | null
content: string
plaintextContent: string
entities: GoogleAnalyzedEntity[]
Expand Down Expand Up @@ -68,4 +73,12 @@ export default class Reflection {
this.sortOrder = sortOrder || 0
this.updatedAt = updatedAt || now
}
toPG() {
return {
...this,
reactjis: this.reactjis.map((r) => `(${r.id},${r.userId})`),
// this is complex because we have to account for escape chars. it's safest to pass in a JSON object & let PG do the conversion for us
entities: toGoogleAnalyzedEntityPG(this.entities)
}
}
}
2 changes: 2 additions & 0 deletions packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export const scalesByTeamId = new RethinkForeignKeyLoaderMaker(
}
)

// Migrating to PG by June 30, 2024
export const retroReflectionsByMeetingId = new RethinkForeignKeyLoaderMaker(
'retroReflections',
'meetingId',
Expand All @@ -179,6 +180,7 @@ export const retroReflectionsByMeetingId = new RethinkForeignKeyLoaderMaker(
}
)

// Migrating to PG by June 30, 2024
export const retroReflectionsByGroupId = new RethinkForeignKeyLoaderMaker(
'retroReflections',
'reflectionGroupId',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const notifications = new RethinkPrimaryKeyLoaderMaker('Notification')
export const organizations = new RethinkPrimaryKeyLoaderMaker('Organization')
export const organizationUsers = new RethinkPrimaryKeyLoaderMaker('OrganizationUser')
export const templateScales = new RethinkPrimaryKeyLoaderMaker('TemplateScale')
// Migrating to PG by June 30, 2024
export const retroReflections = new RethinkPrimaryKeyLoaderMaker('RetroReflection')
export const slackAuths = new RethinkPrimaryKeyLoaderMaker('SlackAuth')
export const slackNotifications = new RethinkPrimaryKeyLoaderMaker('SlackNotification')
Expand Down
9 changes: 8 additions & 1 deletion packages/server/graphql/mutations/createReflection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export default {

// VALIDATION
const normalizedContent = normalizeRawDraftJS(content)
if (normalizedContent.length > 2000) {
return {error: {message: 'Reflection content is too long'}}
}

// RESOLUTION
const plaintextContent = extractTextFromDraftString(normalizedContent)
Expand Down Expand Up @@ -98,7 +101,11 @@ export default {
})

await Promise.all([
pg.insertInto('RetroReflectionGroup').values(reflectionGroup).execute(),
pg
.with('Group', (qc) => qc.insertInto('RetroReflectionGroup').values(reflectionGroup))
.insertInto('RetroReflection')
.values(reflection.toPG())
.execute(),
r.table('RetroReflection').insert(reflection).run()
])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const calculateEngagement = async (meeting: Meeting, dataLoader: DataLoaderWorke
if (getPhase(phases, 'reflect')) {
const reflections = await dataLoader.get('retroReflectionsByMeetingId').load(meetingId)
reflections.forEach(({creatorId, reactjis}) => {
if (!creatorId) return
passiveMembers.delete(creatorId)
reactjis.forEach(({userId}) => {
passiveMembers.delete(userId)
Expand Down
15 changes: 4 additions & 11 deletions packages/server/graphql/mutations/helpers/handleCompletedStage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {AUTO_GROUPING_THRESHOLD, GROUP, REFLECT, VOTE} from 'parabol-client/util
import unlockAllStagesForPhase from 'parabol-client/utils/unlockAllStagesForPhase'
import {r} from 'rethinkdb-ts'
import groupReflections from '../../../../client/utils/smartGroup/groupReflections'
import getRethink from '../../../database/rethinkDriver'
import DiscussStage from '../../../database/types/DiscussStage'
import GenericMeetingStage from '../../../database/types/GenericMeetingStage'
import MeetingRetrospective from '../../../database/types/MeetingRetrospective'
Expand Down Expand Up @@ -30,22 +29,16 @@ const handleCompletedRetrospectiveStage = async (
dataLoader: DataLoaderWorker
) => {
if (stage.phaseType === REFLECT || stage.phaseType === GROUP) {
const data: Record<string, any> = await removeEmptyReflections(meeting)
const data: Record<string, any> = await removeEmptyReflections(meeting, dataLoader)

if (stage.phaseType === REFLECT) {
const r = await getRethink()
const pg = getKysely()

const [reflectionGroups, reflections] = await Promise.all([
const [reflectionGroups, unsortedReflections] = await Promise.all([
dataLoader.get('retroReflectionGroupsByMeetingId').load(meeting.id),
r
.table('RetroReflection')
.getAll(meeting.id, {index: 'meetingId'})
.filter({isActive: true})
.orderBy('createdAt')
.run()
dataLoader.get('retroReflectionsByMeetingId').load(meeting.id)
])

const reflections = unsortedReflections.sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1))
const {reflectionGroupMapping} = groupReflections(reflections.slice(), {
groupingThreshold: AUTO_GROUPING_THRESHOLD
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ export const SlackNotifier = {
dataLoader.get('users').loadNonNull(userId),
dataLoader.get('newMeetings').load(meetingId),
dataLoader.get('retroReflectionGroups').loadNonNull(reflectionGroupId),
r.table('RetroReflection').getAll(reflectionGroupId, {index: 'reflectionGroupId'}).run(),
dataLoader.get('retroReflectionsByGroupId').load(reflectionGroupId),
r
.table('SlackAuth')
.getAll(userId, {index: 'userId'})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import getRethink from '../../../database/rethinkDriver'
import {DataLoaderInstance} from '../../../dataloader/RootDataLoader'
import getKysely from '../../../postgres/getKysely'

const removeEmptyReflectionGroup = async (
reflectionGroupId: string,
oldReflectionGroupId: string
oldReflectionGroupId: string,
dataLoader: DataLoaderInstance
) => {
const r = await getRethink()
const pg = getKysely()
if (!reflectionGroupId) return false
const reflectionCount = await r
.table('RetroReflection')
.getAll(oldReflectionGroupId, {index: 'reflectionGroupId'})
.filter({isActive: true})
.count()
.run()
if (reflectionCount > 0) return
const reflectionsInGroup = await dataLoader
.get('retroReflectionsByGroupId')
.load(oldReflectionGroupId)

if (reflectionsInGroup.length > 0) return

return pg
.updateTable('RetroReflectionGroup')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import extractTextFromDraftString from 'parabol-client/utils/draftjs/extractTextFromDraftString'
import getRethink from '../../../database/rethinkDriver'
import Meeting from '../../../database/types/Meeting'
import {DataLoaderInstance} from '../../../dataloader/RootDataLoader'
import getKysely from '../../../postgres/getKysely'

const removeEmptyReflections = async (meeting: Meeting) => {
const removeEmptyReflections = async (meeting: Meeting, dataLoader: DataLoaderInstance) => {
const r = await getRethink()
const pg = getKysely()
const {id: meetingId} = meeting
const reflections = await r
.table('RetroReflection')
.getAll(meetingId, {index: 'meetingId'})
.filter({isActive: true})
.run()
const reflections = await dataLoader.get('retroReflectionsByMeetingId').load(meetingId)
dataLoader.get('retroReflectionsByMeetingId').clear(meetingId)
dataLoader.get('retroReflections').clearAll()
const emptyReflectionGroupIds = [] as string[]
const emptyReflectionIds = [] as string[]
reflections.forEach((reflection) => {
Expand All @@ -30,6 +29,11 @@ const removeEmptyReflections = async (meeting: Meeting) => {
isActive: false
})
.run(),
pg
.updateTable('RetroReflection')
.set({isActive: false})
.where('id', 'in', emptyReflectionIds)
.execute(),
pg
.updateTable('RetroReflectionGroup')
.set({isActive: false})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import getRethink from '../../../database/rethinkDriver'
import {RDatum} from '../../../database/stricterR'
import MeetingRetrospective from '../../../database/types/MeetingRetrospective'
import TimelineEventRetroComplete from '../../../database/types/TimelineEventRetroComplete'
import getKysely from '../../../postgres/getKysely'
import removeSuggestedAction from '../../../safeMutations/removeSuggestedAction'
import {Logger} from '../../../utils/Logger'
import RecallAIServerManager from '../../../utils/RecallAIServerManager'
Expand Down Expand Up @@ -123,7 +122,6 @@ const safeEndRetrospective = async ({
const {authToken, socketId: mutatorId, dataLoader} = context
const {id: meetingId, phases, facilitatorStageId, teamId} = meeting
const r = await getRethink()
const pg = getKysely()
const operationId = dataLoader.share()
const subOptions = {mutatorId, operationId}
const viewerId = getUserId(authToken)
Expand Down Expand Up @@ -167,11 +165,6 @@ const safeEndRetrospective = async ({
dataLoader.get('teamMembersByTeamId').load(teamId),
removeEmptyTasks(meetingId),
dataLoader.get('meetingTemplates').loadNonNull(templateId),
pg
.deleteFrom('RetroReflectionGroup')
.where('meetingId', '=', meetingId)
.where('isActive', '=', false)
.execute(),
updateTeamInsights(teamId, dataLoader)
])
// wait for removeEmptyTasks before summarizeRetroMeeting
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,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 getKysely from '../../../../postgres/getKysely'
import {GQLContext} from './../../../graphql'
import updateSmartGroupTitle from './updateSmartGroupTitle'
Expand Down Expand Up @@ -32,44 +31,44 @@ const addReflectionToGroup = async (
if (reflectionMeetingId !== meetingId) {
throw new Error('Reflection group not found')
}
const maxSortOrder = await r
.table('RetroReflection')
.getAll(reflectionGroupId, {index: 'reflectionGroupId'})('sortOrder')
.max()
.default(0)
.run()
const reflectionsInNextGroup = await dataLoader
.get('retroReflectionsByGroupId')
.load(reflectionGroupId)
dataLoader.get('retroReflectionsByGroupId').clear(reflectionGroupId)
const maxSortOrder = Math.max(0, ...reflectionsInNextGroup.map((r) => r.sortOrder))

// RESOLUTION
const sortOrder = maxSortOrder + 1 + dndNoise()
await r
.table('RetroReflection')
.get(reflectionId)
.update({
sortOrder,
reflectionGroupId,
updatedAt: now
})
.run()

await Promise.all([
pg
.updateTable('RetroReflection')
.set({
sortOrder,
reflectionGroupId
})
.where('id', '=', reflectionId)
.execute(),
r
.table('RetroReflection')
.get(reflectionId)
.update({
sortOrder,
reflectionGroupId,
updatedAt: now
})
.run()
])
// mutate the dataLoader cache
reflection.sortOrder = sortOrder
reflection.reflectionGroupId = reflectionGroupId
reflection.updatedAt = now

if (oldReflectionGroupId !== reflectionGroupId) {
// ths is not just a reorder within the same group
const {nextReflections, oldReflections} = await r({
nextReflections: r
.table('RetroReflection')
.getAll(reflectionGroupId, {index: 'reflectionGroupId'})
.filter({isActive: true})
.coerceTo('array') as unknown as Reflection[],
oldReflections: r
.table('RetroReflection')
.getAll(oldReflectionGroupId, {index: 'reflectionGroupId'})
.filter({isActive: true})
.coerceTo('array') as unknown as Reflection[]
}).run()
const nextReflections = [...reflectionsInNextGroup, reflection]
const oldReflections = await dataLoader
.get('retroReflectionsByGroupId')
.load(oldReflectionGroupId)

const nextTitle = smartTitle ?? getGroupSmartTitle(nextReflections)
const oldGroupHasSingleReflectionCustomTitle =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,15 @@ const removeReflectionFromGroup = async (reflectionId: string, {dataLoader}: GQL
const reflectionGroup = new ReflectionGroup({meetingId, promptId, sortOrder: newSortOrder})
const {id: reflectionGroupId} = reflectionGroup
await Promise.all([
pg.insertInto('RetroReflectionGroup').values(reflectionGroup).execute(),
pg
.with('Group', (qc) => qc.insertInto('RetroReflectionGroup').values(reflectionGroup))
.updateTable('RetroReflection')
.set({
sortOrder: 0,
reflectionGroupId
})
.where('id', '=', reflectionId)
.execute(),
r
.table('RetroReflection')
.get(reflectionId)
Expand All @@ -62,11 +70,9 @@ const removeReflectionFromGroup = async (reflectionId: string, {dataLoader}: GQL
reflection.reflectionGroupId = reflectionGroupId
const retroMeeting = meeting as MeetingRetrospective
retroMeeting.nextAutoGroupThreshold = null
const oldReflections = await r
.table('RetroReflection')
.getAll(oldReflectionGroupId, {index: 'reflectionGroupId'})
.filter({isActive: true})
.run()
const oldReflections = await dataLoader
.get('retroReflectionsByGroupId')
.load(oldReflectionGroupId)

const nextTitle = getGroupSmartTitle([reflection])
await updateSmartGroupTitle(reflectionGroupId, nextTitle)
Expand Down
Loading
Loading