feat(workspaces): mixpanel update more attributes (#4713)

*  mixpanel recieve more workspace attributes
This commit is contained in:
Daniel Gak Anagrov
2025-05-15 07:20:50 +02:00
committed by GitHub
parent 2294cd1dff
commit fdff51fb51
3 changed files with 106 additions and 21 deletions
@@ -368,6 +368,11 @@ export type CountWorkspaceRoleWithOptionalProjectRole = (args: {
skipUserIds?: string[]
}) => Promise<number>
export type GetWorkspaceSeatsCount = (args: {
workspaceId: string
type?: WorkspaceSeatType
}) => Promise<number>
export type GetUserIdsWithRoleInWorkspace = (
args: {
workspaceId: string
@@ -13,7 +13,9 @@ import {
GetWorkspace,
GetWorkspaceCollaborators,
GetWorkspaceRoleForUser,
GetWorkspaceSeatsCount as GetWorkspaceSeatCount,
GetWorkspaceSeatTypeToProjectRoleMapping,
GetWorkspacesProjectsCounts,
QueryAllWorkspaceProjects,
ValidateWorkspaceMemberProjectRole
} from '@/modules/workspaces/domain/operations'
@@ -32,6 +34,7 @@ import { WorkspaceInviteResourceType } from '@/modules/workspacesCore/domain/con
import {
MaybeNullOrUndefined,
Roles,
SeatTypes,
StreamRoles,
throwUncoveredError,
WorkspaceRoles
@@ -45,6 +48,8 @@ import {
getWorkspaceFactory,
getWorkspaceRoleForUserFactory,
getWorkspaceRolesFactory,
getWorkspaceSeatCountFactory,
getWorkspacesProjectsCountsFactory,
getWorkspaceWithDomainsFactory,
upsertWorkspaceRoleFactory
} from '@/modules/workspaces/repositories/workspaces'
@@ -114,6 +119,9 @@ import { getUserFactory } from '@/modules/core/repositories/users'
import { authorizeResolver } from '@/modules/shared'
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
import { getProjectWorkspaceFactory } from '@/modules/workspaces/repositories/projects'
import { GetWorkspaceModelCount } from '@speckle/shared/dist/commonjs/authz/domain/workspaces/operations.js'
import { getWorkspaceModelCountFactory } from '@/modules/workspaces/services/workspaceLimits'
import { getPaginatedProjectModelsTotalCountFactory } from '@/modules/core/repositories/branches'
const { FF_BILLING_INTEGRATION_ENABLED } = getFeatureFlags()
@@ -486,7 +494,10 @@ export const workspaceTrackingFactory =
getDefaultRegion,
getWorkspacePlan,
getWorkspaceSubscription,
getUserEmails
getUserEmails,
getWorkspaceModelCount,
getWorkspacesProjectCount,
getWorkspaceSeatCount
}: {
getWorkspace: GetWorkspace
countWorkspaceRole: CountWorkspaceRoleWithOptionalProjectRole
@@ -494,6 +505,9 @@ export const workspaceTrackingFactory =
getWorkspacePlan: GetWorkspacePlan
getWorkspaceSubscription: GetWorkspaceSubscription
getUserEmails: FindEmailsByUserId
getWorkspaceModelCount: GetWorkspaceModelCount
getWorkspacesProjectCount: GetWorkspacesProjectsCounts
getWorkspaceSeatCount: GetWorkspaceSeatCount
}) =>
async (params: EventPayload<'workspace.*'> | EventPayload<'gatekeeper.*'>) => {
// temp ignoring tracking for this, if billing is not enabled
@@ -504,27 +518,52 @@ export const workspaceTrackingFactory =
if (!mixpanel) return
const calculateProperties = async (workspace: Workspace) => {
const workspaceId = workspace.id
const [adminCount, memberCount, guestCount, defaultRegion, plan, subscription] =
await Promise.all([
countWorkspaceRole({ workspaceId, workspaceRole: Roles.Workspace.Admin }),
countWorkspaceRole({ workspaceId, workspaceRole: Roles.Workspace.Member }),
countWorkspaceRole({ workspaceId, workspaceRole: Roles.Workspace.Guest }),
getDefaultRegion({ workspaceId }),
getWorkspacePlan({ workspaceId }),
getWorkspaceSubscription({ workspaceId })
])
const seats = subscription?.subscriptionData
? calculateSubscriptionSeats({
subscriptionData: subscription?.subscriptionData
})
: 0
const [
adminCount,
memberCount,
guestCount,
seatsViewerCount,
seatsEditorCount,
defaultRegion,
plan,
subscription,
workspacesProjectCount,
modelCount
] = await Promise.all([
countWorkspaceRole({ workspaceId, workspaceRole: Roles.Workspace.Admin }),
countWorkspaceRole({ workspaceId, workspaceRole: Roles.Workspace.Member }),
countWorkspaceRole({ workspaceId, workspaceRole: Roles.Workspace.Guest }),
getWorkspaceSeatCount({ workspaceId, type: SeatTypes.Editor }),
getWorkspaceSeatCount({ workspaceId, type: SeatTypes.Viewer }),
getDefaultRegion({ workspaceId }),
getWorkspacePlan({ workspaceId }),
getWorkspaceSubscription({ workspaceId }),
getWorkspacesProjectCount({ workspaceIds: [workspaceId] }),
getWorkspaceModelCount({ workspaceId })
])
let seats = 0
let subscriptionBillingInterval = null
let subscriptionCurrentBillingCycleEnd = null
let subscriptionCreatedAt = null
if (subscription !== null) {
seats = calculateSubscriptionSeats({
subscriptionData: subscription.subscriptionData
})
subscriptionBillingInterval = subscription.billingInterval
subscriptionCurrentBillingCycleEnd = subscription.currentBillingCycleEnd
subscriptionCreatedAt = subscription.createdAt
}
return {
name: workspace.name,
description: workspace.description,
domainBasedMembershipProtectionEnabled:
workspace.domainBasedMembershipProtectionEnabled,
discoverabilityEnabled: workspace.discoverabilityEnabled,
defaultRegionKey: defaultRegion?.key,
defaultRegionKey: defaultRegion?.key || null,
teamTotalCount: adminCount + memberCount + guestCount,
teamAdminCount: adminCount,
teamMemberCount: memberCount,
@@ -532,10 +571,16 @@ export const workspaceTrackingFactory =
planName: plan?.name || '',
planStatus: plan?.status || '',
planCreatedAt: plan?.createdAt,
subscriptionBillingInterval: subscription?.billingInterval,
subscriptionCurrentBillingCycleEnd: subscription?.currentBillingCycleEnd,
subscriptionCreatedAt,
subscriptionBillingInterval,
subscriptionCurrentBillingCycleEnd,
seats,
seatsGuest: 0,
seatsViewerCount,
seatsEditorCount,
createdAt: workspace.createdAt,
projectCount: workspacesProjectCount[workspace.id] || 0,
modelCount,
...getBaseTrackingProperties()
}
}
@@ -741,7 +786,16 @@ export const initializeEventListenersFactory =
getUserEmails: findEmailsByUserIdFactory({ db }),
getWorkspace: getWorkspaceFactory({ db }),
getWorkspacePlan,
getWorkspaceSubscription: getWorkspaceSubscriptionFactory({ db })
getWorkspaceSubscription: getWorkspaceSubscriptionFactory({ db }),
getWorkspaceModelCount: getWorkspaceModelCountFactory({
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({
getStreams
}),
getPaginatedProjectModelsTotalCount:
getPaginatedProjectModelsTotalCountFactory({ db })
}),
getWorkspacesProjectCount: getWorkspacesProjectsCountsFactory({ db }),
getWorkspaceSeatCount: getWorkspaceSeatCountFactory({ db })
})(payload)
}),
eventBus.listen('gatekeeper.*', async (payload) => {
@@ -751,7 +805,16 @@ export const initializeEventListenersFactory =
getUserEmails: findEmailsByUserIdFactory({ db }),
getWorkspace: getWorkspaceFactory({ db }),
getWorkspacePlan,
getWorkspaceSubscription: getWorkspaceSubscriptionFactory({ db })
getWorkspaceSubscription: getWorkspaceSubscriptionFactory({ db }),
getWorkspaceModelCount: getWorkspaceModelCountFactory({
queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({
getStreams
}),
getPaginatedProjectModelsTotalCount:
getPaginatedProjectModelsTotalCountFactory({ db })
}),
getWorkspacesProjectCount: getWorkspacesProjectsCountsFactory({ db }),
getWorkspaceSeatCount: getWorkspaceSeatCountFactory({ db })
})(payload)
}),
eventBus.listen(WorkspaceEvents.Authorizing, async ({ payload }) => {
@@ -28,6 +28,7 @@ import {
GetWorkspaceRoleForUser,
GetWorkspaceRoles,
GetWorkspaceRolesForUser,
GetWorkspaceSeatsCount,
GetWorkspaceWithDomains,
GetWorkspaces,
GetWorkspacesProjectsCounts,
@@ -51,7 +52,8 @@ import { WorkspaceInvalidRoleError } from '@/modules/workspaces/errors/workspace
import {
WorkspaceAcl as DbWorkspaceAcl,
WorkspaceDomains,
Workspaces
Workspaces,
WorkspaceSeats
} from '@/modules/workspaces/helpers/db'
import { knex, ServerAcl, StreamAcl, Streams, Users } from '@/modules/core/dbSchema'
import { removePrivateFields } from '@/modules/core/helpers/userHelper'
@@ -502,6 +504,21 @@ export const countWorkspaceRoleWithOptionalProjectRoleFactory =
return parseInt(res.count.toString())
}
export const getWorkspaceSeatCountFactory =
({ db }: { db: Knex }): GetWorkspaceSeatsCount =>
async ({ workspaceId, type }) => {
const query = db(WorkspaceSeats.name).where(
WorkspaceSeats.col.workspaceId,
workspaceId
)
if (type) query.andWhere(WorkspaceSeats.col.type, type)
const [{ count }] = await query.count()
return parseInt(String(count))
}
export const getWorkspaceCreationStateFactory =
({ db }: { db: Knex }): GetWorkspaceCreationState =>
async ({ workspaceId }) => {