ede566eed9
* prep for new resources algo * typescriptifying stuff * minor types fix * migrate to resources col * repo & creation updated, WIP processing/retrieval * WIP invite processing * finished finalization refactor * project invite management * transformed all invites services * fixed up projects & core serverinvites resolvers * test fixes * WIP workspace create GQL & test * basic invite creation test works * a buncha working tests * more tests * cancelation tests * minor invite use refactor * invite retrieval tasks * invite use() works as expected * filtering out broken invites * enabled invite retrieval by token irregardless of who is it for * minor adjustments * tests fix * test config improvements * test env adjustment * extra test case * making resource access limits harder to ignore * linter fixes * eventBus type cleanup * better generic names * refactored serverinvites resource migration * fix(server): better error message in project invite edge case
118 lines
3.8 KiB
TypeScript
118 lines
3.8 KiB
TypeScript
import { addStreamInviteDeclinedActivity } from '@/modules/activitystream/services/streamActivity'
|
|
import { StreamInvalidAccessError } from '@/modules/core/errors/stream'
|
|
import { isResourceAllowed } from '@/modules/core/helpers/token'
|
|
import { getStream } from '@/modules/core/repositories/streams'
|
|
import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/streamAccessService'
|
|
import { ProjectInviteResourceType } from '@/modules/serverinvites/domain/constants'
|
|
import { InviteFinalizingError } from '@/modules/serverinvites/errors'
|
|
import {
|
|
InviteFinalizationAction,
|
|
ProcessFinalizedResourceInvite,
|
|
ValidateResourceInviteBeforeFinalization
|
|
} from '@/modules/serverinvites/services/operations'
|
|
import { Roles } from '@speckle/shared'
|
|
|
|
type ValidateProjectInviteBeforeFinalizationFactoryDeps = {
|
|
getProject: typeof getStream
|
|
}
|
|
|
|
export const validateProjectInviteBeforeFinalizationFactory =
|
|
(
|
|
deps: ValidateProjectInviteBeforeFinalizationFactoryDeps
|
|
): ValidateResourceInviteBeforeFinalization =>
|
|
async (params) => {
|
|
const { getProject } = deps
|
|
const { invite, finalizerUserId, action, finalizerResourceAccessLimits } = params
|
|
|
|
if (invite.resource.resourceType !== ProjectInviteResourceType) {
|
|
throw new InviteFinalizingError(
|
|
'Attempting to finalize non-project invite as project invite',
|
|
{ info: { invite, finalizerUserId } }
|
|
)
|
|
}
|
|
|
|
const project = await getProject({
|
|
streamId: invite.resource.resourceId,
|
|
userId: finalizerUserId
|
|
})
|
|
if (!project) {
|
|
throw new InviteFinalizingError(
|
|
'Attempting to finalize invite to a non-existant project'
|
|
)
|
|
}
|
|
|
|
if (action === InviteFinalizationAction.CANCEL) {
|
|
if (project.role !== Roles.Stream.Owner) {
|
|
throw new InviteFinalizingError(
|
|
'Attempting to cancel invite to a project that the user does not own'
|
|
)
|
|
}
|
|
} else {
|
|
if (project.role) {
|
|
throw new InviteFinalizingError(
|
|
'Attempting to finalize invite to a project that the user already has access to'
|
|
)
|
|
}
|
|
}
|
|
|
|
if (
|
|
!isResourceAllowed({
|
|
resourceId: project.id,
|
|
resourceType: 'project',
|
|
resourceAccessRules: finalizerResourceAccessLimits
|
|
})
|
|
) {
|
|
throw new InviteFinalizingError(
|
|
'You are not allowed to process an invite for this project'
|
|
)
|
|
}
|
|
}
|
|
|
|
type ProcessFinalizedProjectInviteFactoryDeps = {
|
|
getProject: typeof getStream
|
|
addInviteDeclinedActivity: typeof addStreamInviteDeclinedActivity
|
|
addProjectRole: typeof addOrUpdateStreamCollaborator
|
|
}
|
|
|
|
export const processFinalizedProjectInviteFactory =
|
|
(deps: ProcessFinalizedProjectInviteFactoryDeps): ProcessFinalizedResourceInvite =>
|
|
async (params) => {
|
|
const { getProject, addInviteDeclinedActivity, addProjectRole } = deps
|
|
const { invite, finalizerUserId, action } = params
|
|
|
|
const project = await getProject({ streamId: invite.resource.resourceId })
|
|
if (!project) {
|
|
throw new InviteFinalizingError(
|
|
'Attempting to finalize invite to a non-existant project'
|
|
)
|
|
}
|
|
|
|
if (action === InviteFinalizationAction.ACCEPT) {
|
|
try {
|
|
await addProjectRole(
|
|
project.id,
|
|
finalizerUserId,
|
|
invite.resource.role || Roles.Stream.Contributor,
|
|
invite.inviterId,
|
|
null,
|
|
{ fromInvite: true }
|
|
)
|
|
} catch (e) {
|
|
if (!(e instanceof StreamInvalidAccessError)) {
|
|
throw e
|
|
}
|
|
|
|
throw new InviteFinalizingError(
|
|
'Original inviter no longer has the rights to invite you to this project'
|
|
)
|
|
}
|
|
} else if (action === InviteFinalizationAction.DECLINE) {
|
|
await addInviteDeclinedActivity({
|
|
streamId: invite.resource.resourceId,
|
|
inviteTargetId: finalizerUserId,
|
|
inviterId: invite.inviterId,
|
|
stream: project
|
|
})
|
|
}
|
|
}
|