Skip to content

Commit

Permalink
chore: Reuse data loader for analytics (#9239)
Browse files Browse the repository at this point in the history
* chore: Reuse dataloader for analytics

* Cleanup

* Formatting

* More cleanup

* Cleanup

* Confusion
  • Loading branch information
Dschoordsch committed Dec 7, 2023
1 parent 7b8ead0 commit b4821d2
Show file tree
Hide file tree
Showing 74 changed files with 591 additions and 412 deletions.
6 changes: 3 additions & 3 deletions packages/server/billing/helpers/adjustUserCount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const maybeUpdateOrganizationActiveDomain = async (
const changePause = (inactive: boolean) => async (_orgIds: string[], user: IUser) => {
const r = await getRethink()
const {id: userId, email} = user
inactive ? analytics.accountPaused(userId) : analytics.accountUnpaused(userId)
inactive ? analytics.accountPaused(user) : analytics.accountUnpaused(user)
analytics.identify({
userId,
email,
Expand Down Expand Up @@ -110,7 +110,7 @@ const addUser = async (orgIds: string[], user: IUser, dataLoader: DataLoaderWork

const deleteUser = async (orgIds: string[], user: IUser) => {
const r = await getRethink()
orgIds.forEach((orgId) => analytics.userRemovedFromOrg(user.id, orgId))
orgIds.forEach((orgId) => analytics.userRemovedFromOrg(user, orgId))
return r
.table('OrganizationUser')
.getAll(user.id, {index: 'userId'})
Expand Down Expand Up @@ -160,6 +160,6 @@ export default async function adjustUserCount(
.filter((org: RDatum) => org('stripeSubscriptionId').default(null).ne(null))
.run()

handleEnterpriseOrgQuantityChanges(paidOrgs).catch()
handleEnterpriseOrgQuantityChanges(paidOrgs, dataLoader).catch()
handleTeamOrgQuantityChanges(paidOrgs).catch(console.error)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import getRethink from '../../database/rethinkDriver'
import {RDatum} from '../../database/stricterR'
import Organization from '../../database/types/Organization'
import {DataLoaderWorker} from '../../graphql/graphql'
import {analytics} from '../../utils/analytics/analytics'
import {getStripeManager} from '../../utils/stripe'

const sendEnterpriseOverageEvent = async (organization: Organization) => {
const sendEnterpriseOverageEvent = async (
organization: Organization,
dataLoader: DataLoaderWorker
) => {
const r = await getRethink()
const manager = getStripeManager()
const {id: orgId, stripeSubscriptionId} = organization
Expand All @@ -31,15 +35,19 @@ const sendEnterpriseOverageEvent = async (organization: Organization) => {
.nth(0)
.run()
const {id: userId} = billingLeaderOrgUser
analytics.enterpriseOverUserLimit(userId, orgId)
const user = await dataLoader.get('users').loadNonNull(userId)
analytics.enterpriseOverUserLimit(user, orgId)
}
}

const handleEnterpriseOrgQuantityChanges = async (paidOrgs: Organization[]) => {
const handleEnterpriseOrgQuantityChanges = async (
paidOrgs: Organization[],
dataLoader: DataLoaderWorker
) => {
const enterpriseOrgs = paidOrgs.filter((org) => org.tier === 'enterprise')
if (enterpriseOrgs.length === 0) return
for (const org of enterpriseOrgs) {
sendEnterpriseOverageEvent(org).catch()
sendEnterpriseOverageEvent(org, dataLoader).catch()
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/server/billing/helpers/sendTeamsLimitEmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const sendTeamsLimitEmail = (props: Props) => {
const {id: userId, preferredName, email} = user
const {subject, body, html} = teamLimitsEmailCreator({
userId,
email,
orgId,
preferredName,
orgName,
Expand Down
5 changes: 3 additions & 2 deletions packages/server/email/teamLimitsEmailCreator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ const textOnlySummary = (props: Props) => {

interface Props {
userId: string
email: string
preferredName: string
orgId: string
orgName: string
emailType: TeamLimitsEmailType
}

const teamLimitsEmailCreator = (props: Props) => {
const {userId, preferredName, orgId, emailType, orgName} = props
const {userId, email, preferredName, orgId, emailType, orgName} = props
const Email =
emailType === 'locked'
? LockedEmail
Expand All @@ -95,7 +96,7 @@ const teamLimitsEmailCreator = (props: Props) => {
bgColor: PALETTE.SLATE_200
})

analytics.notificationEmailSent(userId, orgId, emailType)
analytics.notificationEmailSent({id: userId, email}, orgId, emailType)

return {
subject,
Expand Down
3 changes: 2 additions & 1 deletion packages/server/graphql/mutations/addAgendaItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default {
if (!isTeamMember(authToken, teamId)) {
return standardError(new Error('Team not found'), {userId: viewerId})
}
const viewer = await dataLoader.get('users').loadNonNull(viewerId)
// VALIDATION
const schema = makeAgendaItemSchema()
const {errors, data: validNewAgendaItem} = schema(newAgendaItem)
Expand All @@ -57,7 +58,7 @@ export default {
.run()

const meetingId = await addAgendaItemToActiveActionMeeting(agendaItemId, teamId, dataLoader)
analytics.addedAgendaItem(viewerId, teamId, meetingId)
analytics.addedAgendaItem(viewer, teamId, meetingId)
const data = {agendaItemId, meetingId}
publish(SubscriptionChannel.TEAM, teamId, 'AddAgendaItemPayload', data, subOptions)
return data
Expand Down
7 changes: 5 additions & 2 deletions packages/server/graphql/mutations/addAtlassianAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export default {
}

// RESOLUTION
const oauthResponse = await AtlassianServerManager.init(code)
const [oauthResponse, viewer] = await Promise.all([
AtlassianServerManager.init(code),
dataLoader.get('users').loadNonNull(viewerId)
])
if (oauthResponse instanceof Error) {
return standardError(new Error(`Jira: ${oauthResponse}`), {userId: viewerId})
}
Expand Down Expand Up @@ -83,7 +86,7 @@ export default {
])
updateRepoIntegrationsCacheByPerms(dataLoader, viewerId, teamId, true)

analytics.integrationAdded(viewerId, teamId, 'jira')
analytics.integrationAdded(viewer, teamId, 'jira')
const data = {teamId, userId: viewerId}
publish(SubscriptionChannel.TEAM, teamId, 'AddAtlassianAuthPayload', data, subOptions)
return data
Expand Down
7 changes: 4 additions & 3 deletions packages/server/graphql/mutations/addComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ const addComment = {
return {error: {message: 'Discussion does not take place in a meeting'}}
}
const meetingMemberId = MeetingMemberId.join(meetingId, viewerId)
const [meeting, viewerMeetingMember] = await Promise.all([
const [meeting, viewerMeetingMember, viewer] = await Promise.all([
dataLoader.get('newMeetings').load(meetingId),
dataLoader.get('meetingMembers').load(meetingMemberId)
dataLoader.get('meetingMembers').load(meetingMemberId),
dataLoader.get('users').loadNonNull(viewerId)
])

if (!viewerMeetingMember) {
Expand Down Expand Up @@ -173,7 +174,7 @@ const addComment = {
)!
const {stages} = containsThreadablePhase
const isAsync = stages.some((stage: GenericMeetingStage) => stage.isAsync)
analytics.commentAdded(viewerId, meeting, isAnonymous, isAsync, !!threadParentId)
analytics.commentAdded(viewer, meeting, isAnonymous, isAsync, !!threadParentId)
publish(SubscriptionChannel.MEETING, meetingId, 'AddCommentSuccess', data, subOptions)
return data
}
Expand Down
11 changes: 7 additions & 4 deletions packages/server/graphql/mutations/addGitHubAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export default {
}

// RESOLUTION
const oAuth2Response = await GitHubServerManager.init(code)
const [oAuth2Response, viewer] = await Promise.all([
GitHubServerManager.init(code),
dataLoader.get('users').loadNonNull(viewerId)
])
if (oAuth2Response instanceof Error) {
return standardError(oAuth2Response, {userId: viewerId})
}
Expand All @@ -50,12 +53,12 @@ export default {
if (error) {
return standardError(error, {userId: viewerId})
}
const {viewer} = data
const {login} = viewer
const {viewer: gitHubViewer} = data
const {login} = gitHubViewer

await upsertGitHubAuth({accessToken, login, teamId, userId: viewerId, scope: scopes})
updateRepoIntegrationsCacheByPerms(dataLoader, viewerId, teamId, true)
analytics.integrationAdded(viewerId, teamId, 'github')
analytics.integrationAdded(viewer, teamId, 'github')

return {teamId, userId: viewerId}
}
Expand Down
10 changes: 5 additions & 5 deletions packages/server/graphql/mutations/addOrg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@ export default {
if (Object.keys(errors).length) {
return standardError(new Error('Failed input validation'), {userId: viewerId})
}
const user = await dataLoader.get('users').load(viewerId)
if (!user) {
const viewer = await dataLoader.get('users').load(viewerId)
if (!viewer) {
return standardError(new Error('Authorization error'), {userId: viewerId})
}

// RESOLUTION
const orgId = generateUID()
const teamId = generateUID()
const {email} = user
const {email} = viewer
await createNewOrg(orgId, orgName, viewerId, email, dataLoader)
await createTeamAndLeader(user, {id: teamId, orgId, isOnboardTeam: false, ...newTeam})
await createTeamAndLeader(viewer, {id: teamId, orgId, isOnboardTeam: false, ...newTeam})

const {tms} = authToken
// MUTATIVE
tms.push(teamId)
analytics.newOrg(viewerId, orgId, teamId, false)
analytics.newOrg(viewer, orgId, teamId, false)
publish(SubscriptionChannel.NOTIFICATION, viewerId, 'AuthTokenPayload', {tms})

const teamMemberId = toTeamMemberId(teamId, viewerId)
Expand Down
4 changes: 2 additions & 2 deletions packages/server/graphql/mutations/addPokerTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const addPokerTemplate = {
r.table('TemplateDimension').insert(newTemplateDimensions).run(),
insertMeetingTemplate(newTemplate)
])
analytics.templateMetrics(viewerId, newTemplate, 'Template Cloned')
analytics.templateMetrics(viewer, newTemplate, 'Template Cloned')
data = {templateId: newTemplate.id}
} else {
if (allTemplates.find((template) => template.name === '*New Template')) {
Expand Down Expand Up @@ -136,7 +136,7 @@ const addPokerTemplate = {
r.table('TemplateDimension').insert(newDimension).run(),
insertMeetingTemplate(newTemplate)
])
analytics.templateMetrics(viewerId, newTemplate, 'Template Created')
analytics.templateMetrics(viewer, newTemplate, 'Template Created')
data = {templateId}
}
publish(SubscriptionChannel.TEAM, teamId, 'AddPokerTemplatePayload', data, subOptions)
Expand Down
15 changes: 9 additions & 6 deletions packages/server/graphql/mutations/addPokerTemplateScale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ const addPokerTemplateScale = {
}

// VALIDATION
const activeScales = await r
.table('TemplateScale')
.getAll(teamId, {index: 'teamId'})
.filter((row: RDatum) => row('removedAt').default(null).eq(null))
.run()
const [activeScales, viewer] = await Promise.all([
r
.table('TemplateScale')
.getAll(teamId, {index: 'teamId'})
.filter((row: RDatum) => row('removedAt').default(null).eq(null))
.run(),
dataLoader.get('users').loadNonNull(viewerId)
])
if (activeScales.length >= Threshold.MAX_POKER_TEMPLATE_SCALES) {
return standardError(new Error('Too many scales'), {userId: viewerId})
}
Expand Down Expand Up @@ -91,7 +94,7 @@ const addPokerTemplateScale = {

const scaleId = newScale.id
const data = {scaleId}
analytics.scaleMetrics(viewerId, newScale, parentScaleId ? 'Scale Cloned' : 'Scale Created')
analytics.scaleMetrics(viewer, newScale, parentScaleId ? 'Scale Cloned' : 'Scale Created')
publish(SubscriptionChannel.TEAM, teamId, 'AddPokerTemplateScalePayload', data, subOptions)
return data
}
Expand Down
4 changes: 2 additions & 2 deletions packages/server/graphql/mutations/addReflectTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const addReflectTemplate = {
r.table('ReflectPrompt').insert(newTemplatePrompts).run(),
insertMeetingTemplate(newTemplate)
])
analytics.templateMetrics(viewerId, newTemplate, 'Template Cloned')
analytics.templateMetrics(viewer, newTemplate, 'Template Cloned')
data = {templateId: newTemplate.id}
} else {
if (allTemplates.find((template) => template.name === '*New Template')) {
Expand Down Expand Up @@ -136,7 +136,7 @@ const addReflectTemplate = {
r.table('ReflectPrompt').insert(newTemplatePrompts).run(),
insertMeetingTemplate(newTemplate)
])
analytics.templateMetrics(viewerId, newTemplate, 'Template Created')
analytics.templateMetrics(viewer, newTemplate, 'Template Created')
data = {templateId}
}
publish(SubscriptionChannel.TEAM, teamId, 'AddReflectTemplatePayload', data, subOptions)
Expand Down
7 changes: 4 additions & 3 deletions packages/server/graphql/mutations/addSlackAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,10 @@ export default {
const {response} = manager
const slackUserId = response.authed_user.id
const defaultChannelId = response.incoming_webhook.channel_id
const [joinConvoRes, userInfoRes] = await Promise.all([
const [joinConvoRes, userInfoRes, viewer] = await Promise.all([
manager.joinConversation(defaultChannelId),
manager.getUserInfo(slackUserId)
manager.getUserInfo(slackUserId),
dataLoader.get('users').loadNonNull(viewerId)
])
if (!userInfoRes.ok) {
return standardError(new Error(userInfoRes.error), {userId: viewerId})
Expand All @@ -126,7 +127,7 @@ export default {
upsertNotifications(viewerId, teamId, teamChannelId, defaultChannelId),
upsertAuth(viewerId, teamId, teamChannelId, userInfoRes.user.profile.display_name, response)
])
analytics.integrationAdded(viewerId, teamId, 'slack')
analytics.integrationAdded(viewer, teamId, 'slack')
const data = {slackAuthId, userId: viewerId}
publish(SubscriptionChannel.TEAM, teamId, 'AddSlackAuthPayload', data, subOptions)
return data
Expand Down
10 changes: 5 additions & 5 deletions packages/server/graphql/mutations/addTeam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ export default {
const {invitees} = args
const orgId = args.newTeam.orgId ?? ''
const viewerId = getUserId(authToken)
const viewer = await dataLoader.get('users').load(viewerId)

if (!(await isUserInOrg(viewerId, orgId, dataLoader))) {
return standardError(new Error('Organization not found'), {userId: viewerId})
}

// VALIDATION
const [orgTeams, organization] = await Promise.all([
const [orgTeams, organization, viewer] = await Promise.all([
getTeamsByOrgIds([orgId], {isArchived: false}),
dataLoader.get('organizations').load(orgId)
dataLoader.get('organizations').load(orgId),
dataLoader.get('users').loadNonNull(viewerId)
])
const orgTeamNames = orgTeams.map((team) => team.name)
const {
Expand Down Expand Up @@ -85,12 +85,12 @@ export default {

// RESOLUTION
const teamId = generateUID()
await createTeamAndLeader(viewer!, {id: teamId, isOnboardTeam: false, ...newTeam})
await createTeamAndLeader(viewer, {id: teamId, isOnboardTeam: false, ...newTeam})

const {tms} = authToken
// MUTATIVE
tms.push(teamId)
analytics.newTeam(viewerId, orgId, teamId, orgTeams.length + 1)
analytics.newTeam(viewer, orgId, teamId, orgTeams.length + 1)
publish(SubscriptionChannel.NOTIFICATION, viewerId, 'AuthTokenPayload', {tms})
const teamMemberId = toTeamMemberId(teamId, viewerId)
const data = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ const addTeamMemberIntegrationAuth = {
}

const providerDbId = IntegrationProviderId.split(providerId)
const integrationProvider = await dataLoader.get('integrationProviders').load(providerDbId)
const [integrationProvider, viewer] = await Promise.all([
dataLoader.get('integrationProviders').load(providerDbId),
dataLoader.get('users').loadNonNull(viewerId)
])
if (!integrationProvider) {
return standardError(
new Error(`Unable to find appropriate integration provider for providerId ${providerId}`),
Expand Down Expand Up @@ -176,7 +179,7 @@ const addTeamMemberIntegrationAuth = {
})
updateRepoIntegrationsCacheByPerms(dataLoader, viewerId, teamId, true)

analytics.integrationAdded(viewerId, teamId, service)
analytics.integrationAdded(viewer, teamId, service)

const data = {userId: viewerId, teamId, service}
return data
Expand Down
7 changes: 5 additions & 2 deletions packages/server/graphql/mutations/archiveOrganization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ export default {
}
}

const organization = await dataLoader.get('organizations').load(orgId)
const [organization, viewer] = await Promise.all([
dataLoader.get('organizations').load(orgId),
dataLoader.get('users').loadNonNull(viewerId)
])
const {tier} = organization
if (tier !== 'starter') {
return standardError(new Error('You must first downgrade before archiving'), {
Expand All @@ -49,7 +52,7 @@ export default {
}

// RESOLUTION
analytics.archiveOrganization(viewerId, orgId)
analytics.archiveOrganization(viewer, orgId)
const teams = await dataLoader.get('teamsByOrgIds').load(orgId)
const teamIds = teams.map(({id}) => id)
const teamArchiveResults = (await Promise.all(
Expand Down
8 changes: 6 additions & 2 deletions packages/server/graphql/mutations/archiveTeam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,16 @@ export default {

// AUTH
const viewerId = getUserId(authToken)
if (!(await isTeamLead(viewerId, teamId, dataLoader)) && !isSuperUser(authToken)) {
const [teamLead, viewer] = await Promise.all([
isTeamLead(viewerId, teamId, dataLoader),
dataLoader.get('users').loadNonNull(viewerId)
])
if (!teamLead && !isSuperUser(authToken)) {
return standardError(new Error('Not team lead'), {userId: viewerId})
}

// RESOLUTION
analytics.archiveTeam(viewerId, teamId)
analytics.archiveTeam(viewer, teamId)
const {team, users, removedSuggestedActionIds} = await safeArchiveTeam(teamId, dataLoader)

if (!team) {
Expand Down

0 comments on commit b4821d2

Please sign in to comment.