gergo/web 2047 user joins the workspace event (#3412)

* feat(gatekeeper): add gatekeeper module feature flag

* feat(gatekeeper): add workspace pricing table domain

* feat(gatekeeper): add checkout session creation

* feat(gatekeeper): verify stripe signature

* wip(gatekeeper): checkout callbacks

* feat(gatekeeper): add unlimited and academia plan types

* refactor(envHelper): getStringFromEnv helper

* chore(gatekeeper): add future todos

* feat(gatekeeper): add productId to the subscription domain

* feat(gatekeeper): add in memory repositories

* feat(gatekeeper): add more errors

* feat(gatekeeper): complete checkout session service

* feat(gatekeeper): add stripe client implementation

* feat(gatekeeper): add checkout session completion webhook callback path

* feat(gendo): fix not needing env vars if gendo module is not enabled

* feat(gatekeeper): require a license for billing

* chore(gatekeeper): cleanup before testing

* feat(gatekeeper): subscriptionData parsing model

* ci: add billing integration and gatekeeper modules to test config

* test(gatekeeper): add checkout service tests

* feat(gatekeeper): make completeCheckout callback idempotent properly

* feat(gatekeeper): move to knex based repositories

* test(gatekeeper): billing repository tests

* feat(gatekeeper): add yearly billing cycle toggle

* feat(ci): add stripe integration context to test job

* feat(billingPage): conditionally render the checkout CTAs

* fix(gatekeeper): remove flaky test condition

* feat(helm): add billing integration feature flag

* WIP billing gql api

* feat(gatekeeper): cancel checkout session api

* feat(gatekeeper): handle existing checkout sessions, when trying to create a new one

* feat(gatekeeper): add workspace plans gql api

* feat(gatekeeper): handle cancelation and subscription updates

* fix(gatekeeper): scope initialization

* fix(gatekeeper): eliminate stripe client import sideeffect

* fix(gatekeeper): eliminate stripe client import sideeffect 2

* feat(gatekeeper): upsize subscription on workspace role change

* feat(shared): add command pattern implementation

* refactor(eventBus): remove return capabilities from the event bus

* refactor(workspaces): use new commandFactory in workspace resolver

* feat(core): facelift taskLock

* feat(gatekeeper): shedule subscription downscale

* feat(gatekeeper): manage subscription downscale

* feat(gatekeeper): get workspace subscriptions, that are about to expire

* feat(gatekeeper): manage subscription downscale

* fix(gatekeeper): do not update subscription to canceled subs

* ci: bump postgres and max connections

* feat(workspaces): fix command factory event bugs
This commit is contained in:
Gergő Jedlicska
2024-10-30 15:51:40 +01:00
committed by GitHub
parent 077587a7c1
commit da7f0dda0e
29 changed files with 1428 additions and 306 deletions
@@ -217,7 +217,7 @@ export type UpdateWorkspaceProjectRole = (
export type EmitWorkspaceEvent = <TEvent extends WorkspaceEvents>(args: {
eventName: TEvent
payload: EventBusPayloads[TEvent]
}) => Promise<unknown[]>
}) => Promise<void>
export type CountProjectsVersionsByWorkspaceId = (args: {
workspaceId: string
@@ -42,7 +42,6 @@ import {
import { createProjectInviteFactory } from '@/modules/serverinvites/services/projectInviteManagement'
import { getInvitationTargetUsersFactory } from '@/modules/serverinvites/services/retrieval'
import { authorizeResolver } from '@/modules/shared'
import { withTransaction } from '@/modules/shared/helpers/dbHelper'
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
import { getEventBus } from '@/modules/shared/services/eventBus'
import { WorkspaceInviteResourceType } from '@/modules/workspaces/domain/constants'
@@ -149,7 +148,10 @@ import { publish } from '@/modules/shared/utils/subscriptions'
import { updateStreamRoleAndNotifyFactory } from '@/modules/core/services/streams/management'
import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users'
import { getServerInfoFactory } from '@/modules/core/repositories/server'
import { commandFactory } from '@/modules/shared/command'
import { withTransaction } from '@/modules/shared/helpers/dbHelper'
const eventBus = getEventBus()
const getServerInfo = getServerInfoFactory({ db })
const getUser = getUserFactory({ db })
const getUsers = getUsersFactory({ db })
@@ -456,36 +458,34 @@ export = FF_WORKSPACES_MODULE_ENABLED
)
if (!role) {
// this is currently not working with the command factory
// TODO: include the onWorkspaceRoleDeletedFactory listener service
const trx = await db.transaction()
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
deleteWorkspaceRole: repoDeleteWorkspaceRoleFactory({ db: trx }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
})
await withTransaction(deleteWorkspaceRole(args.input), trx)
await withTransaction(deleteWorkspaceRole({ workspaceId, userId }), trx)
} else {
if (!isWorkspaceRole(role)) {
throw new WorkspaceInvalidRoleError()
}
const trx = await db.transaction()
const updateWorkspaceRole = updateWorkspaceRoleFactory({
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db: trx }),
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db: trx }),
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({
db: trx
}),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
const updateWorkspaceRole = commandFactory({
db,
eventBus,
operationFactory: ({ db, emit }) =>
updateWorkspaceRoleFactory({
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }),
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }),
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({
db
}),
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
emitWorkspaceEvent: emit
})
})
await withTransaction(
updateWorkspaceRole({ userId, workspaceId, role }),
trx
)
await updateWorkspaceRole({ userId, workspaceId, role })
}
return await getWorkspaceFactory({ db })({ workspaceId })
@@ -559,19 +559,18 @@ export = FF_WORKSPACES_MODULE_ENABLED
})
},
leave: async (_parent, args, context) => {
// this is currently not working with the command factory
// TODO: include the onWorkspaceRoleDeletedFactory listener service
const trx = await db.transaction()
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
deleteWorkspaceRole: repoDeleteWorkspaceRoleFactory({ db: trx }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
})
await withTransaction(
deleteWorkspaceRole({ workspaceId: args.id, userId: context.userId! }),
trx
)
return true
},
invites: () => ({}),
@@ -770,33 +769,33 @@ export = FF_WORKSPACES_MODULE_ENABLED
context.resourceAccessRules
)
const trx = await db.transaction()
const moveProjectToWorkspace = moveProjectToWorkspaceFactory({
getProject: getProjectFactory({ db }),
updateProject: updateProjectFactory({ db: trx }),
upsertProjectRole: upsertProjectRoleFactory({ db: trx }),
getProjectCollaborators: getProjectCollaboratorsFactory({ db }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
getWorkspaceRoleToDefaultProjectRoleMapping:
getWorkspaceRoleToDefaultProjectRoleMappingFactory({
getWorkspace: getWorkspaceFactory({ db })
}),
updateWorkspaceRole: updateWorkspaceRoleFactory({
getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }),
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db: trx }),
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({
db: trx
}),
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db: trx }),
emitWorkspaceEvent: getEventBus().emit
})
const moveProjectToWorkspace = commandFactory({
db,
eventBus,
operationFactory: ({ db, emit }) =>
moveProjectToWorkspaceFactory({
getProject: getProjectFactory({ db }),
updateProject: updateProjectFactory({ db }),
upsertProjectRole: upsertProjectRoleFactory({ db }),
getProjectCollaborators: getProjectCollaboratorsFactory({ db }),
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
getWorkspaceRoleToDefaultProjectRoleMapping:
getWorkspaceRoleToDefaultProjectRoleMappingFactory({
getWorkspace: getWorkspaceFactory({ db })
}),
updateWorkspaceRole: updateWorkspaceRoleFactory({
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }),
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({
db
}),
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }),
emitWorkspaceEvent: emit
})
})
})
return await withTransaction(
moveProjectToWorkspace({ projectId, workspaceId }),
trx
)
return await moveProjectToWorkspace({ projectId, workspaceId })
}
},
Workspace: {
@@ -123,7 +123,6 @@ describe('Workspace join services', () => {
},
emitWorkspaceEvent: async ({ eventName }) => {
firedEvents.push(eventName)
return []
}
})({ userId, workspaceId })
@@ -79,7 +79,6 @@ const buildCreateWorkspaceWithTestContext = (
context.eventData.isCalled = true
context.eventData.eventName = eventName
context.eventData.payload = payload
return []
},
...dependencyOverrides
}
@@ -408,9 +407,7 @@ describe('Workspace services', () => {
let newWorkspaceName
await updateWorkspaceFactory({
getWorkspace: async () => workspace,
emitWorkspaceEvent: async () => {
return []
},
emitWorkspaceEvent: async () => {},
validateSlug: async () => {},
upsertWorkspace: async ({ workspace }) => {
@@ -448,9 +445,7 @@ describe('Workspace services', () => {
await updateWorkspaceFactory({
getWorkspace: async () => workspace,
emitWorkspaceEvent: async () => {
return []
},
emitWorkspaceEvent: async () => {},
validateSlug: async () => {},
upsertWorkspace: async ({ workspace }) => {
updatedWorkspace = workspace
@@ -544,8 +539,6 @@ const buildDeleteWorkspaceRoleAndTestContext = (
break
}
}
return []
},
...dependencyOverrides
}
@@ -622,8 +615,6 @@ const buildUpdateWorkspaceRoleAndTestContext = (
break
}
}
return []
},
...dependencyOverrides
}
@@ -1205,7 +1196,6 @@ describe('Workspace role services', () => {
},
emitWorkspaceEvent: async ({ eventName }) => {
omittedEventName = eventName
return []
},
storeWorkspaceDomain: async ({ workspaceDomain }) => {
storedDomains = workspaceDomain
@@ -1272,9 +1262,7 @@ describe('Workspace role services', () => {
upsertWorkspace: async ({ workspace }) => {
workspaceData = { ...workspaceData, ...workspace }
},
emitWorkspaceEvent: async () => {
return []
},
emitWorkspaceEvent: async () => {},
storeWorkspaceDomain: async ({ workspaceDomain }) => {
insertedDomains.push(workspaceDomain)
}