feat(server): workspace project invite auto-accept (for existing wp members) (#4622)
* WIP ts * DI fix & tests moved to TS * auto-accept seems to work * CR comments
This commit is contained in:
committed by
GitHub
parent
e3a09a932d
commit
c6dcf18bdb
@@ -49,7 +49,7 @@ const addStreamInviteAcceptedActivityFactory =
|
||||
getProjectInviteProject: GetProjectInviteProject
|
||||
}) =>
|
||||
async (payload: EventPayload<typeof ServerInvitesEvents.Finalized>) => {
|
||||
const { invite } = payload.payload
|
||||
const { invite, trueFinalizerUserId } = payload.payload
|
||||
const project = await deps.getProjectInviteProject({ invite })
|
||||
if (!project) return
|
||||
|
||||
@@ -58,14 +58,18 @@ const addStreamInviteAcceptedActivityFactory =
|
||||
getResourceTypeRole(invite.resource, ProjectInviteResourceType) ||
|
||||
Roles.Stream.Contributor
|
||||
|
||||
const differentFinalizer = trueFinalizerUserId !== userTarget.userId
|
||||
|
||||
await deps.saveActivity({
|
||||
streamId: project.id,
|
||||
resourceType: ResourceTypes.Stream,
|
||||
resourceId: project.id,
|
||||
actionType: ActionTypes.Stream.InviteAccepted,
|
||||
userId: userTarget.userId!,
|
||||
info: { inviterUser: invite.inviterId, role },
|
||||
message: `User ${userTarget.userId!} has accepted an invitation to become a ${role}`
|
||||
userId: trueFinalizerUserId,
|
||||
info: { inviterUser: invite.inviterId, role, targetUserId: userTarget.userId! },
|
||||
message: differentFinalizer
|
||||
? `User ${trueFinalizerUserId} has auto-accepted ${userTarget.userId!} invitation to become a ${role}`
|
||||
: `User ${userTarget.userId!} has accepted an invitation to become a ${role}`
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ import { db } from '@/db/knex'
|
||||
import {
|
||||
createStreamFactory,
|
||||
deleteStreamFactory,
|
||||
getStreamFactory
|
||||
getStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
createStreamReturnRecordFactory,
|
||||
@@ -29,8 +30,12 @@ import {
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
@@ -38,6 +43,29 @@ import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
|
||||
const cleanup = async () => {
|
||||
await truncateTables([StreamActivity.name, Users.name])
|
||||
@@ -53,6 +81,52 @@ const createActivitySummary = createActivitySummaryFactory({
|
||||
getActivity: getActivityFactory({ db }),
|
||||
getUser
|
||||
})
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -71,7 +145,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
updateAllInviteTargetsFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
@@ -24,7 +25,8 @@ import { createAndSendInviteFactory } from '@/modules/serverinvites/services/cre
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
@@ -48,7 +50,10 @@ import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
getServerInfoFactory,
|
||||
updateServerInfoFactory
|
||||
@@ -57,6 +62,15 @@ import { temporarilyEnableRateLimiter } from '@/modules/core/tests/ratelimiter.s
|
||||
import { passportAuthenticationCallbackFactory } from '@/modules/auth/services/passportService'
|
||||
import { testLogger as logger } from '@/observability/logging'
|
||||
import { Application } from 'express'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
@@ -64,6 +78,52 @@ const getUsers = getUsersFactory({ db })
|
||||
const createInviteDirectly = createStreamInviteDirectly
|
||||
const findInvite = findInviteFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -82,7 +142,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -205,7 +266,7 @@ describe('Auth @auth', () => {
|
||||
}@speckle.systems`
|
||||
|
||||
const inviterUser = await getUserByEmail({ email: registeredUserEmail })
|
||||
const { token, inviteId } = await createInviteDirectly(
|
||||
const { token, id: inviteId } = await createInviteDirectly(
|
||||
streamInvite
|
||||
? {
|
||||
email: targetEmail,
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
const { buildApolloServer } = require('@/app')
|
||||
const { truncateTables } = require('@/test/hooks')
|
||||
const { gql } = require('graphql-tag')
|
||||
const { createBlobs } = require('@/modules/blobstorage/tests/helpers')
|
||||
const { expect } = require('chai')
|
||||
const { Users, Streams } = require('@/modules/core/dbSchema')
|
||||
const { createAuthedTestContext, executeOperation } = require('@/test/graphqlHelper')
|
||||
const {
|
||||
getStreamFactory,
|
||||
createStreamFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const { createBranchFactory } = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const {
|
||||
findEmailFactory,
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory
|
||||
} = require('@/modules/core/repositories/userEmails')
|
||||
const {
|
||||
requestNewEmailVerificationFactory
|
||||
} = require('@/modules/emails/services/verification/request')
|
||||
const {
|
||||
deleteOldAndInsertNewVerificationFactory
|
||||
} = require('@/modules/emails/repositories')
|
||||
const { renderEmail } = require('@/modules/emails/services/emailRendering')
|
||||
const { sendEmail } = require('@/modules/emails/services/sending')
|
||||
const { createUserFactory } = require('@/modules/core/services/users/management')
|
||||
const {
|
||||
validateAndCreateUserEmailFactory
|
||||
} = require('@/modules/core/services/userEmails')
|
||||
const {
|
||||
finalizeInvitedServerRegistrationFactory
|
||||
} = require('@/modules/serverinvites/services/processing')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
buildInviteEmailContents: buildCoreInviteEmailContentsFactory({
|
||||
getStream
|
||||
}),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
createStream: createStreamFactory({ db }),
|
||||
createBranch: createBranchFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
})
|
||||
|
||||
const findEmail = findEmailFactory({ db })
|
||||
const requestNewEmailVerification = requestNewEmailVerificationFactory({
|
||||
findEmail,
|
||||
getUser: getUserFactory({ db }),
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
const createUser = createUserFactory({
|
||||
getServerInfo,
|
||||
findEmail,
|
||||
storeUser: storeUserFactory({ db }),
|
||||
countAdminUsers: countAdminUsersFactory({ db }),
|
||||
storeUserAcl: storeUserAclFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail,
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
|
||||
describe('Blobs graphql @blobstorage', () => {
|
||||
/** @type {import('@/test/graphqlHelper').ServerAndContext} */
|
||||
let graphqlServer
|
||||
|
||||
const user = {
|
||||
name: 'Baron Von Blubba',
|
||||
email: 'zebarron@bubble.bobble',
|
||||
password: 'bubblesAreMyBlobs'
|
||||
}
|
||||
before(async () => {
|
||||
await truncateTables(['blob_storage', Users.name, Streams.name])
|
||||
user.id = await createUser(user)
|
||||
graphqlServer = {
|
||||
apollo: await buildApolloServer(),
|
||||
context: await createAuthedTestContext(user.id)
|
||||
}
|
||||
})
|
||||
|
||||
it('Stream has blob metadata for a single blob', async () => {
|
||||
const query = gql`
|
||||
query ($streamId: String!, $blobId: String!) {
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
blob(id: $blobId) {
|
||||
id
|
||||
fileName
|
||||
uploadStatus
|
||||
fileSize
|
||||
fileHash
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const streamId = await createStream({ ownerId: user.id })
|
||||
const [blob] = await createBlobs({ streamId, number: 1 })
|
||||
|
||||
const result = await executeOperation(graphqlServer, query, {
|
||||
streamId,
|
||||
blobId: blob.id
|
||||
})
|
||||
|
||||
const blobMetadata = result.data.stream.blob
|
||||
expect(blobMetadata.id).to.equal(blob.id)
|
||||
expect(blobMetadata.fileSize).to.equal(blob.fileSize)
|
||||
expect(blobMetadata.fileHash).to.equal(blob.fileHash)
|
||||
})
|
||||
|
||||
it('Blob metadata collection returns proper summary values', async () => {
|
||||
const query = gql`
|
||||
query ($streamId: String!) {
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
blobs {
|
||||
totalCount
|
||||
totalSize
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const streamId = await createStream({ ownerId: user.id })
|
||||
const number = 10
|
||||
const fileSize = 123
|
||||
await createBlobs({ streamId, number, fileSize })
|
||||
const result = await executeOperation(graphqlServer, query, { streamId })
|
||||
expect(result.data.stream.blobs.totalCount).to.equal(number)
|
||||
expect(result.data.stream.blobs.totalSize).to.equal(number * fileSize)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,244 @@
|
||||
import { buildApolloServer } from '@/app'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import gql from 'graphql-tag'
|
||||
import { createBlobs } from '@/modules/blobstorage/tests/helpers'
|
||||
import { expect } from 'chai'
|
||||
import { Users, Streams } from '@/modules/core/dbSchema'
|
||||
import {
|
||||
createAuthedTestContext,
|
||||
executeOperation,
|
||||
ServerAndContext
|
||||
} from '@/test/graphqlHelper'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} from '@/modules/core/repositories/users'
|
||||
import {
|
||||
findEmailFactory,
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
buildInviteEmailContents: buildCoreInviteEmailContentsFactory({
|
||||
getStream
|
||||
}),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
createStream: createStreamFactory({ db }),
|
||||
createBranch: createBranchFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
})
|
||||
|
||||
const findEmail = findEmailFactory({ db })
|
||||
const requestNewEmailVerification = requestNewEmailVerificationFactory({
|
||||
findEmail,
|
||||
getUser: getUserFactory({ db }),
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
const createUser = createUserFactory({
|
||||
getServerInfo,
|
||||
findEmail,
|
||||
storeUser: storeUserFactory({ db }),
|
||||
countAdminUsers: countAdminUsersFactory({ db }),
|
||||
storeUserAcl: storeUserAclFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail,
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
|
||||
describe('Blobs graphql @blobstorage', () => {
|
||||
let graphqlServer: ServerAndContext
|
||||
|
||||
const user = {
|
||||
name: 'Baron Von Blubba',
|
||||
email: 'zebarron@bubble.bobble',
|
||||
password: 'bubblesAreMyBlobs',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await truncateTables(['blob_storage', Users.name, Streams.name])
|
||||
user.id = await createUser(user)
|
||||
graphqlServer = {
|
||||
apollo: await buildApolloServer(),
|
||||
context: await createAuthedTestContext(user.id)
|
||||
}
|
||||
})
|
||||
|
||||
it('Stream has blob metadata for a single blob', async () => {
|
||||
const query = gql`
|
||||
query ($streamId: String!, $blobId: String!) {
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
blob(id: $blobId) {
|
||||
id
|
||||
fileName
|
||||
uploadStatus
|
||||
fileSize
|
||||
fileHash
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const streamId = await createStream({ ownerId: user.id })
|
||||
const [blob] = await createBlobs({ streamId, number: 1 })
|
||||
|
||||
const result = await executeOperation(graphqlServer, query, {
|
||||
streamId,
|
||||
blobId: blob.id
|
||||
})
|
||||
|
||||
const blobMetadata = result.data!.stream.blob
|
||||
expect(blobMetadata.id).to.equal(blob.id)
|
||||
expect(blobMetadata.fileSize).to.equal(blob.fileSize)
|
||||
expect(blobMetadata.fileHash).to.equal(blob.fileHash)
|
||||
})
|
||||
|
||||
it('Blob metadata collection returns proper summary values', async () => {
|
||||
const query = gql`
|
||||
query ($streamId: String!) {
|
||||
stream(id: $streamId) {
|
||||
id
|
||||
blobs {
|
||||
totalCount
|
||||
totalSize
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const streamId = await createStream({ ownerId: user.id })
|
||||
const number = 10
|
||||
const fileSize = 123
|
||||
await createBlobs({ streamId, number, fileSize })
|
||||
const result = await executeOperation(graphqlServer, query, { streamId })
|
||||
expect(result.data!.stream.blobs.totalCount).to.equal(number)
|
||||
expect(result.data!.stream.blobs.totalSize).to.equal(number * fileSize)
|
||||
})
|
||||
})
|
||||
+215
-175
@@ -1,120 +1,117 @@
|
||||
const expect = require('chai').expect
|
||||
import { expect } from 'chai'
|
||||
|
||||
const crs = require('crypto-random-string')
|
||||
const { buildApolloServer } = require('@/app')
|
||||
const { beforeEachContext } = require('@/test/hooks')
|
||||
const { Roles } = require('@/modules/core/helpers/mainConstants')
|
||||
const { gql } = require('graphql-tag')
|
||||
const {
|
||||
convertBasicStringToDocument
|
||||
} = require('@/modules/core/services/richTextEditorService')
|
||||
const {
|
||||
import crs from 'crypto-random-string'
|
||||
import { buildApolloServer } from '@/app'
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import gql from 'graphql-tag'
|
||||
import { convertBasicStringToDocument } from '@/modules/core/services/richTextEditorService'
|
||||
import {
|
||||
createTestContext,
|
||||
createAuthedTestContext,
|
||||
executeOperation
|
||||
} = require('@/test/graphqlHelper')
|
||||
const {
|
||||
executeOperation,
|
||||
ServerAndContext,
|
||||
ExecuteOperationResponse
|
||||
} from '@/test/graphqlHelper'
|
||||
import {
|
||||
streamResourceCheckFactory,
|
||||
createCommentFactory
|
||||
} = require('@/modules/comments/services')
|
||||
const {
|
||||
} from '@/modules/comments/services'
|
||||
import {
|
||||
checkStreamResourceAccessFactory,
|
||||
markCommentViewedFactory,
|
||||
insertCommentsFactory,
|
||||
insertCommentLinksFactory,
|
||||
deleteCommentFactory,
|
||||
getCommentsResourcesFactory
|
||||
} = require('@/modules/comments/repositories/comments')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
validateInputAttachmentsFactory
|
||||
} = require('@/modules/comments/services/commentTextService')
|
||||
const { getBlobsFactory } = require('@/modules/blobstorage/repositories')
|
||||
const {
|
||||
} from '@/modules/comments/repositories/comments'
|
||||
import { db } from '@/db/knex'
|
||||
import { validateInputAttachmentsFactory } from '@/modules/comments/services/commentTextService'
|
||||
import { getBlobsFactory } from '@/modules/blobstorage/repositories'
|
||||
import {
|
||||
createCommitByBranchIdFactory,
|
||||
createCommitByBranchNameFactory
|
||||
} = require('@/modules/core/services/commit/management')
|
||||
const {
|
||||
} from '@/modules/core/services/commit/management'
|
||||
import {
|
||||
createCommitFactory,
|
||||
insertStreamCommitsFactory,
|
||||
insertBranchCommitsFactory,
|
||||
getCommitsAndTheirBranchIdsFactory
|
||||
} = require('@/modules/core/repositories/commits')
|
||||
const {
|
||||
} from '@/modules/core/repositories/commits'
|
||||
import {
|
||||
getBranchByIdFactory,
|
||||
markCommitBranchUpdatedFactory,
|
||||
getStreamBranchByNameFactory,
|
||||
createBranchFactory
|
||||
} = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
} from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory,
|
||||
updateStreamFactory,
|
||||
grantStreamPermissionsFactory,
|
||||
markCommitStreamUpdatedFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const {
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
getObjectFactory,
|
||||
storeSingleObjectIfNotFoundFactory,
|
||||
getStreamObjectsFactory
|
||||
} = require('@/modules/core/repositories/objects')
|
||||
const {
|
||||
} from '@/modules/core/repositories/objects'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory,
|
||||
legacyUpdateStreamFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const {
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const {
|
||||
} from '@/modules/core/repositories/users'
|
||||
import {
|
||||
findEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
createUserEmailFactory
|
||||
} = require('@/modules/core/repositories/userEmails')
|
||||
const {
|
||||
requestNewEmailVerificationFactory
|
||||
} = require('@/modules/emails/services/verification/request')
|
||||
const {
|
||||
deleteOldAndInsertNewVerificationFactory
|
||||
} = require('@/modules/emails/repositories')
|
||||
const { renderEmail } = require('@/modules/emails/services/emailRendering')
|
||||
const { sendEmail } = require('@/modules/emails/services/sending')
|
||||
const { createUserFactory } = require('@/modules/core/services/users/management')
|
||||
const {
|
||||
validateAndCreateUserEmailFactory
|
||||
} = require('@/modules/core/services/userEmails')
|
||||
const {
|
||||
finalizeInvitedServerRegistrationFactory
|
||||
} = require('@/modules/serverinvites/services/processing')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
const { createObjectFactory } = require('@/modules/core/services/objects/management')
|
||||
const {
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { createObjectFactory } from '@/modules/core/services/objects/management'
|
||||
import {
|
||||
getViewerResourcesFromLegacyIdentifiersFactory,
|
||||
getViewerResourcesForCommentsFactory
|
||||
} = require('@/modules/core/services/commit/viewerResources')
|
||||
} from '@/modules/core/services/commit/viewerResources'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import { SetNonNullable } from 'type-fest'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
@@ -168,6 +165,51 @@ const createCommitByBranchName = createCommitByBranchNameFactory({
|
||||
})
|
||||
|
||||
const getStream = getStreamFactory({ db })
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -186,7 +228,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -232,45 +275,54 @@ const createObject = createObjectFactory({
|
||||
storeSingleObjectIfNotFoundFactory: storeSingleObjectIfNotFoundFactory({ db })
|
||||
})
|
||||
|
||||
function buildCommentInputFromString(textString) {
|
||||
function buildCommentInputFromString(textString: string) {
|
||||
return convertBasicStringToDocument(textString)
|
||||
}
|
||||
|
||||
const testForbiddenResponse = (result) => {
|
||||
const testForbiddenResponse = (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
result: ExecuteOperationResponse<Record<string, any>>
|
||||
) => {
|
||||
expect(result.errors, 'This should have failed').to.exist
|
||||
expect(result.errors.length).to.be.above(0)
|
||||
expect(result.errors[0].extensions.code).to.match(
|
||||
expect(result.errors!.length).to.be.above(0)
|
||||
expect(result.errors![0].extensions!.code).to.match(
|
||||
/(STREAM_INVALID_ACCESS_ERROR|FORBIDDEN|UNAUTHORIZED_ACCESS_ERROR)/
|
||||
)
|
||||
}
|
||||
|
||||
const testResult = (shouldSucceed, result, successTests) => {
|
||||
const testResult = (
|
||||
shouldSucceed: boolean,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
result: ExecuteOperationResponse<Record<string, any>>,
|
||||
successTests: (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
result: SetNonNullable<ExecuteOperationResponse<Record<string, any>>, 'data'>
|
||||
) => void
|
||||
) => {
|
||||
if (shouldSucceed) {
|
||||
expect(result.errors, 'This should not have failed').to.not.exist
|
||||
successTests(result)
|
||||
successTests(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
result as SetNonNullable<ExecuteOperationResponse<Record<string, any>>, 'data'>
|
||||
)
|
||||
} else {
|
||||
testForbiddenResponse(result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* apollo: import('@/test/graphqlHelper').ServerAndContext,
|
||||
* resources: {
|
||||
* streamId: string,
|
||||
* objectId: string,
|
||||
* commentId: string,
|
||||
* testActorId: string
|
||||
* },
|
||||
* shouldSucceed: boolean,
|
||||
* streamId: string
|
||||
* }} TestContext
|
||||
*/
|
||||
type TestContext = {
|
||||
apollo: ServerAndContext
|
||||
resources: {
|
||||
streamId: string
|
||||
objectId: string
|
||||
commentId: string
|
||||
testActorId: string
|
||||
}
|
||||
shouldSucceed: boolean
|
||||
streamId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const writeComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const writeComment = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -294,10 +346,11 @@ const writeComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const broadcastViewerActivity = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const broadcastViewerActivity = async ({
|
||||
apollo,
|
||||
resources,
|
||||
shouldSucceed
|
||||
}: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -320,10 +373,11 @@ const broadcastViewerActivity = async ({ apollo, resources, shouldSucceed }) =>
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const broadcastCommentActivity = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const broadcastCommentActivity = async ({
|
||||
apollo,
|
||||
resources,
|
||||
shouldSucceed
|
||||
}: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -346,10 +400,7 @@ const broadcastCommentActivity = async ({ apollo, resources, shouldSucceed }) =>
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const viewAComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const viewAComment = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -367,13 +418,10 @@ const viewAComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const archiveMyComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const archiveMyComment = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const context = apollo.context
|
||||
const { id: commentId } = await createComment({
|
||||
userId: context.userId,
|
||||
userId: context!.userId!,
|
||||
input: {
|
||||
streamId: resources.streamId,
|
||||
text: buildCommentInputFromString('i wrote this myself'),
|
||||
@@ -399,10 +447,11 @@ const archiveMyComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const archiveOthersComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const archiveOthersComment = async ({
|
||||
apollo,
|
||||
resources,
|
||||
shouldSucceed
|
||||
}: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -420,12 +469,9 @@ const archiveOthersComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const editMyComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const editMyComment = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const { id: commentId } = await createComment({
|
||||
userId: apollo.context.userId,
|
||||
userId: apollo.context!.userId!,
|
||||
input: {
|
||||
streamId: resources.streamId,
|
||||
text: buildCommentInputFromString('i wrote this myself'),
|
||||
@@ -458,10 +504,7 @@ const editMyComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const editOthersComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const editOthersComment = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -485,10 +528,7 @@ const editOthersComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const replyToAComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const replyToAComment = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -514,10 +554,7 @@ const replyToAComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const queryComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const queryComment = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const res = await executeOperation(
|
||||
apollo,
|
||||
gql`
|
||||
@@ -547,10 +584,7 @@ const queryComment = async ({ apollo, resources, shouldSucceed }) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const queryComments = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const queryComments = async ({ apollo, resources, shouldSucceed }: TestContext) => {
|
||||
const object = {
|
||||
foo: 123,
|
||||
bar: crs({ length: 5 })
|
||||
@@ -599,14 +633,17 @@ const queryComments = async ({ apollo, resources, shouldSucceed }) => {
|
||||
)
|
||||
testResult(shouldSucceed, res, (res) => {
|
||||
expect(res.data.comments.totalCount).to.be.equal(numberOfComments)
|
||||
expect(res.data.comments.items.map((i) => i.id)).to.be.equalInAnyOrder(commentIds)
|
||||
expect(
|
||||
res.data.comments.items.map((i: { id: string }) => i.id)
|
||||
).to.deep.equalInAnyOrder(commentIds)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const queryStreamCommentCount = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const queryStreamCommentCount = async ({
|
||||
apollo,
|
||||
resources,
|
||||
shouldSucceed
|
||||
}: TestContext) => {
|
||||
await createComment({
|
||||
userId: resources.testActorId,
|
||||
input: {
|
||||
@@ -635,10 +672,11 @@ const queryStreamCommentCount = async ({ apollo, resources, shouldSucceed }) =>
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const queryObjectCommentCount = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const queryObjectCommentCount = async ({
|
||||
apollo,
|
||||
resources,
|
||||
shouldSucceed
|
||||
}: TestContext) => {
|
||||
const objectId = await createObject({
|
||||
streamId: resources.streamId,
|
||||
object: {
|
||||
@@ -675,10 +713,11 @@ const queryObjectCommentCount = async ({ apollo, resources, shouldSucceed }) =>
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const queryCommitCommentCount = async ({ apollo, resources, shouldSucceed }) => {
|
||||
const queryCommitCommentCount = async ({
|
||||
apollo,
|
||||
resources,
|
||||
shouldSucceed
|
||||
}: TestContext) => {
|
||||
const objectId = await createObject({
|
||||
streamId: resources.streamId,
|
||||
object: {
|
||||
@@ -722,14 +761,11 @@ const queryCommitCommentCount = async ({ apollo, resources, shouldSucceed }) =>
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {TestContext} param0
|
||||
*/
|
||||
const queryCommitCollectionCommentCount = async ({
|
||||
apollo,
|
||||
resources,
|
||||
shouldSucceed
|
||||
}) => {
|
||||
}: TestContext) => {
|
||||
const objectId = await createObject({
|
||||
streamId: resources.streamId,
|
||||
object: {
|
||||
@@ -772,16 +808,13 @@ const queryCommitCollectionCommentCount = async ({
|
||||
)
|
||||
testResult(shouldSucceed, res, (res) => {
|
||||
res.data.otherUser.commits.items
|
||||
.map((i) => i.commentCount)
|
||||
.map((commentCount) => {
|
||||
.map((i: { commentCount: number }) => i.commentCount)
|
||||
.map((commentCount: number) => {
|
||||
expect(commentCount).to.be.greaterThanOrEqual(1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const actions = ['queryCommitCommentCount', 'queryCommitCollectionCommentCount']
|
||||
|
||||
describe('Graphql @comments', () => {
|
||||
// this user will be admin by default
|
||||
// it will be used to create all resources, that the other actors can
|
||||
@@ -789,59 +822,69 @@ describe('Graphql @comments', () => {
|
||||
const myTestActor = {
|
||||
name: 'Gergo Jedlicska',
|
||||
email: 'gergo@jedlicska.com',
|
||||
password: 'sn3aky-1337-b1m'
|
||||
password: 'sn3aky-1337-b1m',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const chadTheEngineer = {
|
||||
name: 'Chad the Engineer',
|
||||
email: 'chad@engineering.acme',
|
||||
password: 'tryingNotToBeACadMonkey',
|
||||
role: Roles.Server.User
|
||||
role: Roles.Server.User,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const archived = {
|
||||
name: 'The Balrog of Morgoth',
|
||||
email: 'durinsbane@moria.bridge',
|
||||
role: Roles.Server.ArchivedUser
|
||||
password: 'tryingNotToBeACadMonkey',
|
||||
role: Roles.Server.ArchivedUser,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const ownedStream = {
|
||||
name: 'stream owner',
|
||||
isPublic: false,
|
||||
role: Roles.Stream.Owner
|
||||
role: Roles.Stream.Owner,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const contributorStream = {
|
||||
name: 'contributions are welcome',
|
||||
isPublic: false,
|
||||
role: Roles.Stream.Contributor
|
||||
role: Roles.Stream.Contributor,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const reviewerStream = {
|
||||
name: 'no work, just talk',
|
||||
isPublic: false,
|
||||
role: Roles.Stream.Reviewer
|
||||
role: Roles.Stream.Reviewer,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const noAccessStream = {
|
||||
name: 'aint nobody canna cross it',
|
||||
isPublic: false,
|
||||
role: null
|
||||
role: null,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const publicStream = {
|
||||
name: 'come take a look',
|
||||
isPublic: true,
|
||||
role: null
|
||||
role: null,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const publicStreamWithPublicComments = {
|
||||
name: 'the gossip protocol',
|
||||
isPublic: true,
|
||||
role: null
|
||||
role: null,
|
||||
id: ''
|
||||
}
|
||||
|
||||
const testData = [
|
||||
const testData = <const>[
|
||||
{
|
||||
user: chadTheEngineer,
|
||||
streamData: [
|
||||
@@ -1116,11 +1159,8 @@ describe('Graphql @comments', () => {
|
||||
}`, () => {
|
||||
userContext.streamData.forEach((streamContext) => {
|
||||
const stream = streamContext.stream
|
||||
let resources
|
||||
/**
|
||||
* @type {import('@/test/graphqlHelper').ServerAndContext}
|
||||
*/
|
||||
let apollo
|
||||
let resources: TestContext['resources']
|
||||
let apollo: ServerAndContext
|
||||
|
||||
before(async () => {
|
||||
apollo = {
|
||||
@@ -55,7 +55,8 @@ import { getBlobsFactory } from '@/modules/blobstorage/repositories'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory,
|
||||
markCommitStreamUpdatedFactory
|
||||
markCommitStreamUpdatedFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
createCommitByBranchIdFactory,
|
||||
@@ -88,7 +89,9 @@ import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
@@ -111,7 +114,10 @@ import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { createObjectFactory } from '@/modules/core/services/objects/management'
|
||||
import type express from 'express'
|
||||
@@ -130,6 +136,15 @@ import {
|
||||
getViewerResourcesFromLegacyIdentifiersFactory
|
||||
} from '@/modules/core/services/commit/viewerResources'
|
||||
import { StreamRecord } from '@/modules/core/helpers/types'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
type LegacyCommentRecord = CommentRecord & {
|
||||
total_count: string
|
||||
@@ -223,6 +238,51 @@ const createCommitByBranchName = createCommitByBranchNameFactory({
|
||||
getBranchById: getBranchByIdFactory({ db })
|
||||
})
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -241,7 +301,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -77,8 +77,12 @@ import {
|
||||
} from '@/modules/multiregion/utils/dbSelector'
|
||||
import {
|
||||
deleteAllResourceInvitesFactory,
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
@@ -94,11 +98,75 @@ import {
|
||||
import { has } from 'lodash'
|
||||
import { throwIfAuthNotOk } from '@/modules/shared/helpers/errorHelper'
|
||||
import { withOperationLogging } from '@/observability/domain/businessLogging'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStreamReturnRecord = createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
@@ -116,7 +184,8 @@ const createStreamReturnRecord = createStreamReturnRecordFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -48,9 +48,13 @@ import {
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import {
|
||||
deleteAllResourceInvitesFactory,
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
queryAllResourceInvitesFactory
|
||||
queryAllResourceInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import db from '@/db/knex'
|
||||
import { getInvitationTargetUsersFactory } from '@/modules/serverinvites/services/retrieval'
|
||||
@@ -75,6 +79,24 @@ import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/use
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { adminOverrideEnabled } from '@/modules/shared/helpers/envHelper'
|
||||
import { withOperationLogging } from '@/observability/domain/businessLogging'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
@@ -84,6 +106,52 @@ const getFavoriteStreamsCollection = getFavoriteStreamsCollectionFactory({
|
||||
getFavoritedStreamsPage: getFavoritedStreamsPageFactory({ db })
|
||||
})
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStreamReturnRecord = createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
@@ -101,7 +169,8 @@ const createStreamReturnRecord = createStreamReturnRecordFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -208,8 +208,9 @@ export const addOrUpdateStreamCollaboratorFactory =
|
||||
eventName: ServerInvitesEvents.Finalized,
|
||||
payload: {
|
||||
invite: fromInvite,
|
||||
finalizerUserId: addedById,
|
||||
accept: true
|
||||
finalizerUserId: userId,
|
||||
accept: true,
|
||||
trueFinalizerUserId: addedById
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -27,7 +27,8 @@ import {
|
||||
getStreamFactory,
|
||||
createStreamFactory,
|
||||
markBranchStreamUpdatedFactory,
|
||||
markCommitStreamUpdatedFactory
|
||||
markCommitStreamUpdatedFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
createCommitByBranchIdFactory,
|
||||
@@ -52,7 +53,9 @@ import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
@@ -75,12 +78,24 @@ import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { getPaginatedStreamBranchesFactory } from '@/modules/core/services/branch/retrieval'
|
||||
import { createObjectFactory } from '@/modules/core/services/objects/management'
|
||||
import { ensureError } from '@speckle/shared'
|
||||
import { ModelEvents } from '@/modules/core/domain/branches/events'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const db = knex
|
||||
const Commits = () => knex('commits')
|
||||
@@ -125,6 +140,51 @@ const createCommitByBranchName = createCommitByBranchNameFactory({
|
||||
getBranchById: getBranchByIdFactory({ db })
|
||||
})
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -143,7 +203,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -37,7 +37,8 @@ import {
|
||||
getStreamFactory,
|
||||
getCommitStreamFactory,
|
||||
createStreamFactory,
|
||||
markCommitStreamUpdatedFactory
|
||||
markCommitStreamUpdatedFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
getObjectFactory,
|
||||
@@ -53,7 +54,9 @@ import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
@@ -76,7 +79,10 @@ import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
getBranchCommitsTotalCountByNameFactory,
|
||||
@@ -85,6 +91,15 @@ import {
|
||||
import { createObjectFactory } from '@/modules/core/services/objects/management'
|
||||
import { ensureError } from '@speckle/shared'
|
||||
import { VersionEvents } from '@/modules/core/domain/commits/events'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
@@ -139,6 +154,51 @@ const updateCommitAndNotify = updateCommitAndNotifyFactory({
|
||||
})
|
||||
const getStreamCommitCount = getStreamCommitCountFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -157,7 +217,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
+138
-84
@@ -1,78 +1,124 @@
|
||||
/* instanbul ignore file */
|
||||
const expect = require('chai').expect
|
||||
import { expect } from 'chai'
|
||||
|
||||
const { buildApolloServer } = require('@/app')
|
||||
const { StreamFavorites, Streams, Users } = require('@/modules/core/dbSchema')
|
||||
const { truncateTables } = require('@/test/hooks')
|
||||
const { gql } = require('graphql-tag')
|
||||
const { sleep } = require('@/test/helpers')
|
||||
const {
|
||||
import { buildApolloServer } from '@/app'
|
||||
import { StreamFavorites, Streams, Users } from '@/modules/core/dbSchema'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import gql from 'graphql-tag'
|
||||
import { sleep } from '@/test/helpers'
|
||||
import {
|
||||
createAuthedTestContext,
|
||||
createTestContext,
|
||||
executeOperation
|
||||
} = require('@/test/graphqlHelper')
|
||||
const {
|
||||
executeOperation,
|
||||
ServerAndContext
|
||||
} from '@/test/graphqlHelper'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const { createBranchFactory } = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const {
|
||||
} from '@/modules/core/repositories/users'
|
||||
import {
|
||||
findEmailFactory,
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory
|
||||
} = require('@/modules/core/repositories/userEmails')
|
||||
const {
|
||||
requestNewEmailVerificationFactory
|
||||
} = require('@/modules/emails/services/verification/request')
|
||||
const {
|
||||
deleteOldAndInsertNewVerificationFactory
|
||||
} = require('@/modules/emails/repositories')
|
||||
const { renderEmail } = require('@/modules/emails/services/emailRendering')
|
||||
const { sendEmail } = require('@/modules/emails/services/sending')
|
||||
const { createUserFactory } = require('@/modules/core/services/users/management')
|
||||
const {
|
||||
validateAndCreateUserEmailFactory
|
||||
} = require('@/modules/core/services/userEmails')
|
||||
const {
|
||||
finalizeInvitedServerRegistrationFactory
|
||||
} = require('@/modules/serverinvites/services/processing')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -91,7 +137,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -205,29 +252,35 @@ const totalOwnedStreamsFavoritesNew = gql`
|
||||
describe('Favorite streams', () => {
|
||||
const myPubStream = {
|
||||
name: 'My Stream 1',
|
||||
isPublic: false
|
||||
isPublic: false,
|
||||
id: ''
|
||||
}
|
||||
const myStream = {
|
||||
name: 'My Stream 2',
|
||||
isPublic: true
|
||||
isPublic: true,
|
||||
id: ''
|
||||
}
|
||||
const notMyStream = {
|
||||
name: 'Not My Stream 1',
|
||||
isPublic: false
|
||||
isPublic: false,
|
||||
id: ''
|
||||
}
|
||||
const notMyPubStream = {
|
||||
name: 'Not My Stream 2',
|
||||
isPublic: true
|
||||
isPublic: true,
|
||||
id: ''
|
||||
}
|
||||
const me = {
|
||||
name: 'Itsa Me',
|
||||
email: 'me@example.org',
|
||||
password: 'sn3aky-1337-b1m'
|
||||
password: 'sn3aky-1337-b1m',
|
||||
id: ''
|
||||
}
|
||||
const otherGuy = {
|
||||
name: 'Some Other DUde',
|
||||
email: 'otherguy@example.org',
|
||||
password: 'sn3aky-1337-b1m'
|
||||
password: 'sn3aky-1337-b1m',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async function () {
|
||||
@@ -259,9 +312,9 @@ describe('Favorite streams', () => {
|
||||
|
||||
describe('when authenticated', () => {
|
||||
/** @type {import('@/test/graphqlHelper').ServerAndContext} */
|
||||
let apollo
|
||||
let apollo: ServerAndContext
|
||||
|
||||
const favoriteStream = async (sid, favorited) =>
|
||||
const favoriteStream = async (sid: string, favorited: boolean) =>
|
||||
await executeOperation(apollo, favoriteMutationGql, { sid, favorited })
|
||||
|
||||
before(async () => {
|
||||
@@ -274,7 +327,7 @@ describe('Favorite streams', () => {
|
||||
await StreamFavorites.knex().truncate()
|
||||
})
|
||||
|
||||
const accessibleStreamIds = [
|
||||
const accessibleStreamIds = <const>[
|
||||
[() => myPubStream.id, 'owned and public'],
|
||||
[() => myStream.id, 'owned and not public'],
|
||||
[() => notMyPubStream.id, 'not owned, but public']
|
||||
@@ -292,7 +345,7 @@ describe('Favorite streams', () => {
|
||||
expect(result.errors).to.not.be.ok
|
||||
expect(result.data?.streamFavorite?.favoritedDate).to.be.a('date')
|
||||
expect(result.data?.streamFavorite?.favoritedDate.getTime()).to.satisfy(
|
||||
(t) => t > beforeTime && t < afterTime
|
||||
(t: number) => t > beforeTime && t < afterTime
|
||||
)
|
||||
expect(result.data?.streamFavorite?.id).to.equal(streamId)
|
||||
expect(result.data?.streamFavorite?.favoritesCount).to.equal(1)
|
||||
@@ -302,19 +355,19 @@ describe('Favorite streams', () => {
|
||||
it("can't be favorited if not owned and not public", async () => {
|
||||
const result = await favoriteStream(notMyStream.id, true)
|
||||
|
||||
expect(result.data.streamFavorite).to.not.be.ok
|
||||
expect(result.data!.streamFavorite).to.not.be.ok
|
||||
expect(result.errors).to.have.lengthOf(1)
|
||||
expect(result.errors.at(0).message).to.contain("doesn't have access")
|
||||
expect(result.errors!.at(0)!.message).to.contain("doesn't have access")
|
||||
})
|
||||
|
||||
describe('and favorited', () => {
|
||||
const favoritedStream = {
|
||||
name: 'Favorited Stream',
|
||||
isPublic: true
|
||||
isPublic: true,
|
||||
id: ''
|
||||
}
|
||||
|
||||
/** @type {{favoritedDate: Date, favoritesCount: number, id: string}} */
|
||||
let favoritingResults
|
||||
let favoritingResults: { favoritedDate: Date; favoritesCount: number; id: string }
|
||||
|
||||
before(async () => {
|
||||
favoritedStream.id = await createStream({ ...favoritedStream, ownerId: me.id })
|
||||
@@ -345,12 +398,12 @@ describe('Favorite streams', () => {
|
||||
|
||||
describe('and being queried', () => {
|
||||
const favoritableStreams = [
|
||||
{ name: 'Random 1', isPublic: true },
|
||||
{ name: 'Random 2', isPublic: true },
|
||||
{ name: 'Random 2', isPublic: true }
|
||||
{ name: 'Random 1', isPublic: true, id: '' },
|
||||
{ name: 'Random 2', isPublic: true, id: '' },
|
||||
{ name: 'Random 2', isPublic: true, id: '' }
|
||||
]
|
||||
|
||||
const getFavorites = async (cursor, limit = 10) =>
|
||||
const getFavorites = async (cursor: string | null, limit = 10) =>
|
||||
await executeOperation(apollo, favoriteStreamsQueryGql, { cursor, limit })
|
||||
|
||||
const favoritedStreamIds = () => favoritableStreams.map((s) => s.id)
|
||||
@@ -380,7 +433,7 @@ describe('Favorite streams', () => {
|
||||
)
|
||||
|
||||
expect(data).to.be.ok
|
||||
expect(data.otherUser?.favoriteStreams).to.not.be.ok
|
||||
expect(data!.otherUser?.favoriteStreams).to.not.be.ok
|
||||
expect((errors || []).map((e) => e.message).join()).to.match(
|
||||
/cannot view another user's favorite streams/i
|
||||
)
|
||||
@@ -394,22 +447,24 @@ describe('Favorite streams', () => {
|
||||
expect(results.data?.activeUser?.favoriteStreams?.items).to.have.lengthOf(
|
||||
ids.length
|
||||
)
|
||||
expect(results.data.activeUser.favoriteStreams.totalCount).to.equal(ids.length)
|
||||
expect(results.data.activeUser.favoriteStreams.cursor).to.be.a('string')
|
||||
expect(results.data!.activeUser.favoriteStreams.totalCount).to.equal(ids.length)
|
||||
expect(results.data!.activeUser.favoriteStreams.cursor).to.be.a('string')
|
||||
})
|
||||
|
||||
it('are paginated correctly', async () => {
|
||||
let nextCursor = null
|
||||
let returnedStreamIds = []
|
||||
let returnedStreamIds: string[] = []
|
||||
|
||||
const getPaginatedAndAssert = async (nextCursor) => {
|
||||
const getPaginatedAndAssert = async (nextCursor: string | null) => {
|
||||
const results = await getFavorites(nextCursor, 1)
|
||||
expect(results.errors).to.not.be.ok
|
||||
expect(results.data?.activeUser?.favoriteStreams).to.be.ok
|
||||
|
||||
return {
|
||||
cursor: results.data.activeUser.favoriteStreams.cursor,
|
||||
sids: results.data.activeUser.favoriteStreams.items.map((i) => i.id)
|
||||
cursor: results.data!.activeUser.favoriteStreams.cursor,
|
||||
sids: results.data!.activeUser.favoriteStreams.items.map(
|
||||
(i: { id: string }) => i.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,8 +511,7 @@ describe('Favorite streams', () => {
|
||||
})
|
||||
|
||||
describe('when not authenticated', () => {
|
||||
/** @type {import('@/test/graphqlHelper').ServerAndContext} */
|
||||
let apollo
|
||||
let apollo: ServerAndContext
|
||||
|
||||
before(async () => {
|
||||
apollo = {
|
||||
@@ -472,15 +526,15 @@ describe('Favorite streams', () => {
|
||||
favorited: true
|
||||
})
|
||||
|
||||
expect(result.data.streamFavorite).to.not.be.ok
|
||||
expect(result.data!.streamFavorite).to.not.be.ok
|
||||
expect(result.errors).to.have.lengthOf(1)
|
||||
expect(result.errors.at(0).message).to.contain('Must provide an auth token')
|
||||
expect(result.errors!.at(0)!.message).to.contain('Must provide an auth token')
|
||||
})
|
||||
|
||||
it("can't be retrieved", async () => {
|
||||
const result = await executeOperation(apollo, favoriteStreamsQueryGql)
|
||||
|
||||
expect(result.data.activeUser).to.be.null
|
||||
expect(result.data!.activeUser).to.be.null
|
||||
expect(result.errors).to.not.be.ok
|
||||
})
|
||||
})
|
||||
+129
-69
@@ -1,71 +1,115 @@
|
||||
/* istanbul ignore file */
|
||||
const expect = require('chai').expect
|
||||
import { expect } from 'chai'
|
||||
|
||||
const { beforeEachContext } = require('@/test/hooks')
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
|
||||
const { validateScopes, authorizeResolver } = require('@/modules/shared')
|
||||
const { buildContext } = require('@/modules/shared/middleware')
|
||||
const { Roles, Scopes } = require('@speckle/shared')
|
||||
const { throwForNotHavingServerRole } = require('@/modules/shared/authz')
|
||||
const { ForbiddenError } = require('@/modules/shared/errors')
|
||||
const {
|
||||
import { validateScopes, authorizeResolver } from '@/modules/shared'
|
||||
import { buildContext } from '@/modules/shared/middleware'
|
||||
import { AvailableRoles, Roles, Scopes, ServerRoles } from '@speckle/shared'
|
||||
import { throwForNotHavingServerRole } from '@/modules/shared/authz'
|
||||
import { ForbiddenError } from '@/modules/shared/errors'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const { createBranchFactory } = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const {
|
||||
} from '@/modules/core/repositories/users'
|
||||
import {
|
||||
findEmailFactory,
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory
|
||||
} = require('@/modules/core/repositories/userEmails')
|
||||
const {
|
||||
requestNewEmailVerificationFactory
|
||||
} = require('@/modules/emails/services/verification/request')
|
||||
const {
|
||||
deleteOldAndInsertNewVerificationFactory
|
||||
} = require('@/modules/emails/repositories')
|
||||
const { renderEmail } = require('@/modules/emails/services/emailRendering')
|
||||
const { sendEmail } = require('@/modules/emails/services/sending')
|
||||
const { createUserFactory } = require('@/modules/core/services/users/management')
|
||||
const {
|
||||
validateAndCreateUserEmailFactory
|
||||
} = require('@/modules/core/services/userEmails')
|
||||
const {
|
||||
finalizeInvitedServerRegistrationFactory
|
||||
} = require('@/modules/serverinvites/services/processing')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
const { mockAdminOverride } = require('@/test/mocks/global')
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { mockAdminOverride } from '@/test/mocks/global'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { Request } from 'express'
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
@@ -89,7 +133,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -134,7 +179,7 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
})
|
||||
|
||||
it('Validate scopes', async () => {
|
||||
await validateScopes()
|
||||
await validateScopes(undefined, undefined as unknown as string)
|
||||
.then(() => {
|
||||
throw new Error('This should have been rejected')
|
||||
})
|
||||
@@ -156,14 +201,17 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
|
||||
await validateScopes(['a', 'b'], 'b') // should pass
|
||||
})
|
||||
;[
|
||||
['BS header', { req: { headers: { authorization: 'Bearer BS' } } }],
|
||||
['Null header', { req: { headers: { authorization: null } } }],
|
||||
['Undefined header', { req: { headers: { authorization: undefined } } }],
|
||||
;(<const>[
|
||||
['BS header', { req: { headers: { authorization: 'Bearer BS' } } as Request }],
|
||||
[
|
||||
'Null header',
|
||||
{ req: { headers: { authorization: null as string | null } } as Request }
|
||||
],
|
||||
['Undefined header', { req: { headers: { authorization: undefined } } as Request }],
|
||||
['BS token', { token: 'Bearer BS' }],
|
||||
['Null token', { token: null }],
|
||||
['Undefined token', { token: undefined }]
|
||||
].map(([caseName, contextInput]) =>
|
||||
]).map(([caseName, contextInput]) =>
|
||||
it(`Should create proper context ${caseName}`, async () => {
|
||||
const res = await buildContext(contextInput)
|
||||
expect(res.auth).to.equal(false)
|
||||
@@ -182,7 +230,10 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
expect('You do not have the required server role').to.equal(err.message)
|
||||
)
|
||||
|
||||
await throwForNotHavingServerRole({ auth: true, role: 'HACZOR' }, '133TCR3w')
|
||||
await throwForNotHavingServerRole(
|
||||
{ auth: true, role: 'HACZOR' as ServerRoles },
|
||||
'133TCR3w' as ServerRoles
|
||||
)
|
||||
.then(() => {
|
||||
throw new Error('This should have been rejected')
|
||||
})
|
||||
@@ -192,7 +243,7 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
|
||||
await throwForNotHavingServerRole(
|
||||
{ auth: true, role: Roles.Server.Admin },
|
||||
'133TCR3w'
|
||||
'133TCR3w' as ServerRoles
|
||||
)
|
||||
.then(() => {
|
||||
throw new Error('This should have been rejected')
|
||||
@@ -209,14 +260,19 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
})
|
||||
|
||||
it('Resolver Authorization Should fail nicely when roles & resources are wanky', async () => {
|
||||
await authorizeResolver(null, 'foo', 'bar')
|
||||
await authorizeResolver(null, 'foo', 'bar' as AvailableRoles, null)
|
||||
.then(() => {
|
||||
throw new Error('This should have been rejected')
|
||||
})
|
||||
.catch((err) => expect('Unknown role: bar').to.equal(err.message))
|
||||
|
||||
// this caught me out, but streams:read is not a valid role for now
|
||||
await authorizeResolver('foo', 'bar', Scopes.Streams.Read)
|
||||
await authorizeResolver(
|
||||
'foo',
|
||||
'bar' as AvailableRoles,
|
||||
Scopes.Streams.Read as AvailableRoles,
|
||||
null
|
||||
)
|
||||
.then(() => {
|
||||
throw new Error('This should have been rejected')
|
||||
})
|
||||
@@ -226,21 +282,25 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
describe('Authorize resolver ', () => {
|
||||
const myStream = {
|
||||
name: 'My Stream 2',
|
||||
isPublic: true
|
||||
isPublic: true,
|
||||
id: ''
|
||||
}
|
||||
const notMyStream = {
|
||||
name: 'Not My Stream 1',
|
||||
isPublic: false
|
||||
isPublic: false,
|
||||
id: ''
|
||||
}
|
||||
const serverOwner = {
|
||||
name: 'Itsa Me',
|
||||
email: 'me@example.org',
|
||||
password: 'sn3aky-1337-b1m'
|
||||
password: 'sn3aky-1337-b1m',
|
||||
id: ''
|
||||
}
|
||||
const otherGuy = {
|
||||
name: 'Some Other DUde',
|
||||
email: 'otherguy@example.org',
|
||||
password: 'sn3aky-1337-b1m'
|
||||
password: 'sn3aky-1337-b1m',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async function () {
|
||||
@@ -290,7 +350,7 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
Roles.Stream.Contributor,
|
||||
null
|
||||
)
|
||||
throw 'This should have thrown'
|
||||
throw new Error('This should have thrown')
|
||||
} catch (e) {
|
||||
expect(e instanceof ForbiddenError)
|
||||
}
|
||||
@@ -315,7 +375,7 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
Roles.Stream.Contributor,
|
||||
null
|
||||
)
|
||||
throw 'This should have thrown'
|
||||
throw new Error('This should have thrown')
|
||||
} catch (e) {
|
||||
expect(e instanceof ForbiddenError)
|
||||
}
|
||||
@@ -331,7 +391,7 @@ describe('Generic AuthN & AuthZ controller tests', () => {
|
||||
Roles.Stream.Contributor,
|
||||
null
|
||||
)
|
||||
throw 'This should have thrown'
|
||||
throw new Error('This should have thrown')
|
||||
} catch (e) {
|
||||
expect(e instanceof ForbiddenError)
|
||||
}
|
||||
+199
-106
@@ -1,75 +1,65 @@
|
||||
/* istanbul ignore file */
|
||||
/* eslint-disable camelcase */
|
||||
const expect = require('chai').expect
|
||||
const assert = require('assert')
|
||||
const { cloneDeep, times, random, padStart } = require('lodash')
|
||||
import { expect } from 'chai'
|
||||
import assert from 'assert'
|
||||
import { cloneDeep, times, random, padStart } from 'lodash'
|
||||
|
||||
const { beforeEachContext } = require('@/test/hooks')
|
||||
const { getAnIdForThisOnePlease } = require('@/test/helpers')
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { getAnIdForThisOnePlease } from '@/test/helpers'
|
||||
|
||||
const {
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const { createBranchFactory } = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const {
|
||||
} from '@/modules/core/repositories/users'
|
||||
import {
|
||||
findEmailFactory,
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory
|
||||
} = require('@/modules/core/repositories/userEmails')
|
||||
const {
|
||||
requestNewEmailVerificationFactory
|
||||
} = require('@/modules/emails/services/verification/request')
|
||||
const {
|
||||
deleteOldAndInsertNewVerificationFactory
|
||||
} = require('@/modules/emails/repositories')
|
||||
const { renderEmail } = require('@/modules/emails/services/emailRendering')
|
||||
const { sendEmail } = require('@/modules/emails/services/sending')
|
||||
const { createUserFactory } = require('@/modules/core/services/users/management')
|
||||
const {
|
||||
validateAndCreateUserEmailFactory
|
||||
} = require('@/modules/core/services/userEmails')
|
||||
const {
|
||||
finalizeInvitedServerRegistrationFactory
|
||||
} = require('@/modules/serverinvites/services/processing')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
const {
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
createObjectFactory,
|
||||
createObjectsBatchedAndNoClosuresFactory,
|
||||
createObjectsFactory
|
||||
} = require('@/modules/core/services/objects/management')
|
||||
const {
|
||||
} from '@/modules/core/services/objects/management'
|
||||
import {
|
||||
storeSingleObjectIfNotFoundFactory,
|
||||
storeObjectsIfNotFoundFactory,
|
||||
getFormattedObjectFactory,
|
||||
@@ -77,7 +67,17 @@ const {
|
||||
getObjectChildrenFactory,
|
||||
getObjectChildrenQueryFactory,
|
||||
getStreamObjectsFactory
|
||||
} = require('@/modules/core/repositories/objects')
|
||||
} from '@/modules/core/repositories/objects'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import { ObjectRecord } from '@/modules/core/helpers/types'
|
||||
|
||||
const sampleCommit = JSON.parse(`{
|
||||
"Objects": [
|
||||
@@ -106,6 +106,52 @@ const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -124,7 +170,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -180,12 +227,14 @@ describe('Objects @core-objects', () => {
|
||||
const userOne = {
|
||||
name: 'Dimitrie Stefanescu',
|
||||
email: 'didimitrie43@example.org',
|
||||
password: 'sn3aky-1337-b1m'
|
||||
password: 'sn3aky-1337-b1m',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const stream = {
|
||||
name: 'Test Streams',
|
||||
description: 'Whatever goes in here usually...'
|
||||
description: 'Whatever goes in here usually...',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
@@ -202,8 +251,8 @@ describe('Objects @core-objects', () => {
|
||||
|
||||
const objCount_1 = 10
|
||||
const objCount_2 = 1000
|
||||
const objs = []
|
||||
const objs2 = []
|
||||
const objs: Array<Record<string, unknown> & { id?: string }> = []
|
||||
const objs2: Array<Record<string, unknown> & { id?: string }> = []
|
||||
|
||||
it(`Should create ${objCount_1} objects`, async () => {
|
||||
for (let i = 0; i < objCount_1; i++) {
|
||||
@@ -258,7 +307,7 @@ describe('Objects @core-objects', () => {
|
||||
]).reduce((obj, [key, value]) => {
|
||||
obj[key] = value
|
||||
return obj
|
||||
}, {})
|
||||
}, {} as Record<string, unknown>)
|
||||
}
|
||||
const id = await createObject({ streamId: stream.id, object: obj })
|
||||
expect(id).to.be.ok
|
||||
@@ -272,20 +321,20 @@ describe('Objects @core-objects', () => {
|
||||
it('Should get more objects', async () => {
|
||||
const myObjs = await getObjects(
|
||||
stream.id,
|
||||
objs.map((o) => o.id)
|
||||
objs.map((o) => o.id!)
|
||||
)
|
||||
expect(myObjs).to.have.lengthOf(objs.length)
|
||||
|
||||
const match1 = myObjs.find((o) => o.id === objs[0].id)
|
||||
expect(match1).to.not.be.null
|
||||
expect(match1.id).to.equal(objs[0].id)
|
||||
expect(match1!.id).to.equal(objs[0].id)
|
||||
|
||||
const match2 = myObjs.find((o) => o.id === objs[2].id)
|
||||
expect(match2).to.not.be.null
|
||||
expect(match2.id).to.equal(objs[2].id)
|
||||
expect(match2!.id).to.equal(objs[2].id)
|
||||
})
|
||||
|
||||
let parentObjectId
|
||||
let parentObjectId: string
|
||||
|
||||
it('Should get object children', async () => {
|
||||
const objs_1 = createManyObjects(100, 'noise__')
|
||||
@@ -368,7 +417,7 @@ describe('Objects @core-objects', () => {
|
||||
{ field: 'test.value', operator: '<', value: 24 },
|
||||
{ verb: 'OR', field: 'test.value', operator: '=', value: 42 }
|
||||
],
|
||||
orderBy: { field: 'test.value', direction: 'asc' }
|
||||
orderBy: { field: 'test.value' as keyof ObjectRecord, direction: 'asc' }
|
||||
})
|
||||
|
||||
const test2 = await getObjectChildrenQuery({
|
||||
@@ -381,7 +430,7 @@ describe('Objects @core-objects', () => {
|
||||
{ field: 'test.value', operator: '<', value: 24 },
|
||||
{ verb: 'OR', field: 'test.value', operator: '=', value: 42 }
|
||||
],
|
||||
orderBy: { field: 'test.value', direction: 'asc' },
|
||||
orderBy: { field: 'test.value' as keyof ObjectRecord, direction: 'asc' },
|
||||
cursor: test.cursor
|
||||
})
|
||||
|
||||
@@ -403,14 +452,19 @@ describe('Objects @core-objects', () => {
|
||||
expect(test.totalCount).to.equal(23)
|
||||
expect(test2.totalCount).to.equal(23)
|
||||
|
||||
expect(test.objects[0].data.test.value).to.be.below(test.objects[1].data.test.value)
|
||||
expect(test2.objects[0].data.test.value).to.be.below(
|
||||
test2.objects[1].data.test.value
|
||||
)
|
||||
const testObjects = test.objects as unknown as Array<{
|
||||
data: { test: { value: number } }
|
||||
}>
|
||||
const test2Objects = test2.objects as unknown as Array<{
|
||||
data: { test: { value: number } }
|
||||
}>
|
||||
|
||||
expect(testObjects[0].data.test.value).to.be.below(testObjects[1].data.test.value)
|
||||
expect(test2Objects[0].data.test.value).to.be.below(test2Objects[1].data.test.value)
|
||||
|
||||
// continuity
|
||||
expect(test.objects[test.objects.length - 1].data.test.value + 1).to.equal(
|
||||
test2.objects[0].data.test.value
|
||||
expect(testObjects[testObjects.length - 1].data.test.value + 1).to.equal(
|
||||
test2Objects[0].data.test.value
|
||||
)
|
||||
})
|
||||
|
||||
@@ -424,7 +478,7 @@ describe('Objects @core-objects', () => {
|
||||
{ field: 'similar', operator: '>=', value: 0 },
|
||||
{ field: 'similar', operator: '<', value: 100 }
|
||||
],
|
||||
orderBy: { field: 'similar', direction: 'asc' },
|
||||
orderBy: { field: 'similar' as keyof ObjectRecord, direction: 'asc' },
|
||||
limit: 5
|
||||
})
|
||||
|
||||
@@ -436,7 +490,7 @@ describe('Objects @core-objects', () => {
|
||||
{ field: 'similar', operator: '>=', value: 0 },
|
||||
{ field: 'similar', operator: '<', value: 100 }
|
||||
],
|
||||
orderBy: { field: 'similar', direction: 'asc' },
|
||||
orderBy: { field: 'similar' as keyof ObjectRecord, direction: 'asc' },
|
||||
cursor: test3.cursor,
|
||||
limit: 5
|
||||
})
|
||||
@@ -453,17 +507,24 @@ describe('Objects @core-objects', () => {
|
||||
expect(test3.totalCount).to.equal(100)
|
||||
expect(test4.totalCount).to.equal(100)
|
||||
|
||||
expect(test3.objects[0].data.similar).to.be.below(test3.objects[1].data.similar) // 0, 1, 1, 1, ...
|
||||
expect(test4.objects[0].data.similar).to.be.below(test4.objects[3].data.similar)
|
||||
const test3Objects = test3.objects as unknown as Array<{
|
||||
data: { similar: number }
|
||||
}>
|
||||
const test4Objects = test4.objects as unknown as Array<{
|
||||
data: { similar: number }
|
||||
}>
|
||||
|
||||
expect(test3Objects[0].data.similar).to.be.below(test3Objects[1].data.similar) // 0, 1, 1, 1, ...
|
||||
expect(test4Objects[0].data.similar).to.be.below(test4Objects[3].data.similar)
|
||||
|
||||
// continuity (in reverse)
|
||||
expect(test3.objects[test3.objects.length - 1].data.similar).to.equal(
|
||||
test3.objects[test3.objects.length - 2].data.similar + 1
|
||||
expect(test3Objects[test3Objects.length - 1].data.similar).to.equal(
|
||||
test3Objects[test3Objects.length - 2].data.similar + 1
|
||||
)
|
||||
expect(test3.objects[test3.objects.length - 1].data.similar).to.equal(
|
||||
test4.objects[0].data.similar
|
||||
expect(test3Objects[test3Objects.length - 1].data.similar).to.equal(
|
||||
test4Objects[0].data.similar
|
||||
)
|
||||
expect(test4.objects[1].data.similar).to.equal(test4.objects[2].data.similar - 1)
|
||||
expect(test4Objects[1].data.similar).to.equal(test4Objects[2].data.similar - 1)
|
||||
})
|
||||
|
||||
it('should query object children with no results ', async () => {
|
||||
@@ -474,7 +535,7 @@ describe('Objects @core-objects', () => {
|
||||
{ field: 'test.value', operator: '>=', value: 10 },
|
||||
{ field: 'test.value', operator: '<', value: 9 }
|
||||
],
|
||||
orderBy: { field: 'test.value', direction: 'desc' }
|
||||
orderBy: { field: 'test.value' as keyof ObjectRecord, direction: 'desc' }
|
||||
})
|
||||
|
||||
expect(test.totalCount).to.equal(0)
|
||||
@@ -494,7 +555,7 @@ describe('Objects @core-objects', () => {
|
||||
},
|
||||
{ field: 'test.value', operator: '<', value: 9 }
|
||||
],
|
||||
orderBy: { field: 'test.value', direction: 'desc' }
|
||||
orderBy: { field: 'test.value' as keyof ObjectRecord, direction: 'desc' }
|
||||
})
|
||||
assert.fail('sql injections are bad for health')
|
||||
} catch {
|
||||
@@ -509,7 +570,7 @@ describe('Objects @core-objects', () => {
|
||||
limit: 5,
|
||||
select: ['test.value', 'nest.duck'],
|
||||
query: [{ field: 'test.value', operator: '<', value: 10 }],
|
||||
orderBy: { field: 'nest.duck', direction: 'desc' }
|
||||
orderBy: { field: 'nest.duck' as keyof ObjectRecord, direction: 'desc' }
|
||||
})
|
||||
|
||||
const test2 = await getObjectChildrenQuery({
|
||||
@@ -518,12 +579,18 @@ describe('Objects @core-objects', () => {
|
||||
limit: 5,
|
||||
select: ['test.value', 'nest.duck'],
|
||||
query: [{ field: 'test.value', operator: '<', value: 10 }],
|
||||
orderBy: { field: 'nest.duck', direction: 'desc' },
|
||||
orderBy: { field: 'nest.duck' as keyof ObjectRecord, direction: 'desc' },
|
||||
cursor: test.cursor
|
||||
})
|
||||
|
||||
expect(test.objects[0].data.nest.duck).to.equal(true)
|
||||
expect(test2.objects[test2.objects.length - 1].data.nest.duck).to.equal(false) // last duck should be false
|
||||
const testObjects = test.objects as unknown as Array<{
|
||||
data: { test: { value: number }; nest: { duck: boolean } }
|
||||
}>
|
||||
const test2Objects = test2.objects as unknown as Array<{
|
||||
data: { test: { value: number }; nest: { duck: boolean } }
|
||||
}>
|
||||
expect(testObjects[0].data.nest.duck).to.equal(true)
|
||||
expect(test2Objects[test2Objects.length - 1].data.nest.duck).to.equal(false) // last duck should be false
|
||||
})
|
||||
|
||||
it('should query children and sort them by a string value ', async () => {
|
||||
@@ -534,7 +601,7 @@ describe('Objects @core-objects', () => {
|
||||
objectId: parentObjectId,
|
||||
limit: 5,
|
||||
query: [{ field: 'test.value', operator: '<', value: limVal }],
|
||||
orderBy: { field: 'name', direction: 'asc' }
|
||||
orderBy: { field: 'name' as keyof ObjectRecord, direction: 'asc' }
|
||||
})
|
||||
|
||||
const test2 = await getObjectChildrenQuery({
|
||||
@@ -542,18 +609,25 @@ describe('Objects @core-objects', () => {
|
||||
objectId: parentObjectId,
|
||||
limit: 5,
|
||||
query: [{ field: 'test.value', operator: '<', value: limVal }],
|
||||
orderBy: { field: 'name', direction: 'asc' },
|
||||
orderBy: { field: 'name' as keyof ObjectRecord, direction: 'asc' },
|
||||
cursor: test.cursor
|
||||
})
|
||||
|
||||
expect(test.objects.length).to.equal(5)
|
||||
expect(test.cursor).to.be.a('string')
|
||||
|
||||
expect(test.objects[0].data.name).to.equal('mr. 0')
|
||||
expect(test.objects[1].data.name).to.equal('mr. 1')
|
||||
expect(test.objects[2].data.name).to.equal('mr. 10') // remember kids, this is a lexicographical sort
|
||||
expect(test.objects[4].data.name).to.equal('mr. 12')
|
||||
expect(test2.objects[0].data.name).to.equal('mr. 13')
|
||||
const testObjects = test.objects as unknown as Array<{
|
||||
data: { name: string; test: { value: number } }
|
||||
}>
|
||||
const test2Objects = test2.objects as unknown as Array<{
|
||||
data: { name: string; test: { value: number } }
|
||||
}>
|
||||
|
||||
expect(testObjects[0].data.name).to.equal('mr. 0')
|
||||
expect(testObjects[1].data.name).to.equal('mr. 1')
|
||||
expect(testObjects[2].data.name).to.equal('mr. 10') // remember kids, this is a lexicographical sort
|
||||
expect(testObjects[4].data.name).to.equal('mr. 12')
|
||||
expect(test2Objects[0].data.name).to.equal('mr. 13')
|
||||
})
|
||||
|
||||
it('should query children and sort them by id by default ', async () => {
|
||||
@@ -588,41 +662,53 @@ describe('Objects @core-objects', () => {
|
||||
streamId: stream.id,
|
||||
objectId: parentObjectId,
|
||||
limit: 2,
|
||||
orderBy: { field: 'test.value', direction: 'desc' }
|
||||
orderBy: { field: 'test.value' as keyof ObjectRecord, direction: 'desc' }
|
||||
})
|
||||
|
||||
const test2 = await getObjectChildrenQuery({
|
||||
streamId: stream.id,
|
||||
objectId: parentObjectId,
|
||||
limit: 2,
|
||||
orderBy: { field: 'test.value', direction: 'desc' },
|
||||
orderBy: { field: 'test.value' as keyof ObjectRecord, direction: 'desc' },
|
||||
cursor: test.cursor
|
||||
})
|
||||
|
||||
expect(test.objects[1].data.test.value).to.equal(
|
||||
test2.objects[0].data.test.value + 1
|
||||
) // continuity check
|
||||
const testObjects = test.objects as unknown as Array<{
|
||||
data: { test: { value: number } }
|
||||
}>
|
||||
const test2Objects = test2.objects as unknown as Array<{
|
||||
data: { test: { value: number } }
|
||||
}>
|
||||
|
||||
expect(testObjects[1].data.test.value).to.equal(test2Objects[0].data.test.value + 1) // continuity check
|
||||
|
||||
const test3 = await getObjectChildrenQuery({
|
||||
streamId: stream.id,
|
||||
objectId: parentObjectId,
|
||||
limit: 50,
|
||||
orderBy: { field: 'nest.duck', direction: 'desc' }
|
||||
orderBy: { field: 'nest.duck' as keyof ObjectRecord, direction: 'desc' }
|
||||
})
|
||||
|
||||
const test4 = await getObjectChildrenQuery({
|
||||
streamId: stream.id,
|
||||
objectId: parentObjectId,
|
||||
limit: 50,
|
||||
orderBy: { field: 'nest.duck', direction: 'desc' },
|
||||
orderBy: { field: 'nest.duck' as keyof ObjectRecord, direction: 'desc' },
|
||||
cursor: test3.cursor
|
||||
})
|
||||
|
||||
expect(test3.objects[49].data.nest.duck).to.equal(true)
|
||||
expect(test4.objects[0].data.nest.duck).to.equal(false)
|
||||
const test3Objects = test3.objects as unknown as Array<{
|
||||
data: { nest: { duck: boolean } }
|
||||
}>
|
||||
const test4Objects = test4.objects as unknown as Array<{
|
||||
data: { nest: { duck: boolean } }
|
||||
}>
|
||||
|
||||
expect(test3Objects[49].data.nest.duck).to.equal(true)
|
||||
expect(test4Objects[0].data.nest.duck).to.equal(false)
|
||||
})
|
||||
|
||||
let commitId
|
||||
let commitId: string
|
||||
it('should batch create objects', async () => {
|
||||
const objs = createManyObjects(3333, 'perlin merlin magic')
|
||||
commitId = objs[0].id
|
||||
@@ -641,6 +727,7 @@ describe('Objects @core-objects', () => {
|
||||
|
||||
it('should stream objects back', (done) => {
|
||||
let tcount = 0
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
getObjectChildrenStream({ streamId: stream.id, objectId: commitId }).then(
|
||||
(stream) => {
|
||||
stream.on('data', () => tcount++)
|
||||
@@ -656,7 +743,7 @@ describe('Objects @core-objects', () => {
|
||||
this.timeout(5000)
|
||||
const objs = createManyObjects(5000, 'perlin merlin magic')
|
||||
|
||||
function shuffleArray(array) {
|
||||
function shuffleArray(array: Array<unknown>) {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1))
|
||||
;[array[i], array[j]] = [array[j], array[i]]
|
||||
@@ -686,13 +773,18 @@ describe('Objects @core-objects', () => {
|
||||
})
|
||||
})
|
||||
|
||||
function createManyObjects(num, noise) {
|
||||
function createManyObjects(num: number, noise: string | number) {
|
||||
num = num || 10000
|
||||
noise = noise || Math.random() * 100
|
||||
|
||||
const objs = []
|
||||
|
||||
const base = { name: 'base bastard 2', noise, __closure: {} }
|
||||
const base = {
|
||||
name: 'base bastard 2',
|
||||
noise,
|
||||
__closure: {} as Record<string, number>,
|
||||
id: ''
|
||||
}
|
||||
objs.push(base)
|
||||
let k = 0
|
||||
|
||||
@@ -706,7 +798,8 @@ function createManyObjects(num, noise) {
|
||||
objArr: [{ a: i }, { b: i * i }, { c: true }],
|
||||
noise,
|
||||
sortValueA: i,
|
||||
sortValueB: i * 0.42 * i
|
||||
sortValueB: i * 0.42 * i,
|
||||
id: ''
|
||||
}
|
||||
|
||||
if (i % 3 === 0) k++
|
||||
+165
-88
@@ -1,84 +1,130 @@
|
||||
/* istanbul ignore file */
|
||||
const expect = require('chai').expect
|
||||
const request = require('supertest')
|
||||
import { expect } from 'chai'
|
||||
import request from 'supertest'
|
||||
|
||||
const assert = require('assert')
|
||||
const crypto = require('crypto')
|
||||
import assert from 'assert'
|
||||
import crypto from 'crypto'
|
||||
|
||||
const { beforeEachContext } = require('@/test/hooks')
|
||||
const { createManyObjects } = require('@/test/helpers')
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { createManyObjects } from '@/test/helpers'
|
||||
|
||||
const { Scopes } = require('@speckle/shared')
|
||||
const {
|
||||
import { Scopes } from '@speckle/shared'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const { createBranchFactory } = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const {
|
||||
} from '@/modules/core/repositories/users'
|
||||
import {
|
||||
findEmailFactory,
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory
|
||||
} = require('@/modules/core/repositories/userEmails')
|
||||
const {
|
||||
requestNewEmailVerificationFactory
|
||||
} = require('@/modules/emails/services/verification/request')
|
||||
const {
|
||||
deleteOldAndInsertNewVerificationFactory
|
||||
} = require('@/modules/emails/repositories')
|
||||
const { renderEmail } = require('@/modules/emails/services/emailRendering')
|
||||
const { sendEmail } = require('@/modules/emails/services/sending')
|
||||
const { createUserFactory } = require('@/modules/core/services/users/management')
|
||||
const {
|
||||
validateAndCreateUserEmailFactory
|
||||
} = require('@/modules/core/services/userEmails')
|
||||
const {
|
||||
finalizeInvitedServerRegistrationFactory
|
||||
} = require('@/modules/serverinvites/services/processing')
|
||||
const { createPersonalAccessTokenFactory } = require('@/modules/core/services/tokens')
|
||||
const {
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { createPersonalAccessTokenFactory } from '@/modules/core/services/tokens'
|
||||
import {
|
||||
storeTokenScopesFactory,
|
||||
storeApiTokenFactory,
|
||||
storeTokenResourceAccessDefinitionsFactory,
|
||||
storePersonalApiTokenFactory
|
||||
} = require('@/modules/core/repositories/tokens')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
const cryptoRandomString = require('crypto-random-string')
|
||||
} from '@/modules/core/repositories/tokens'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import type Express from 'express'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -97,7 +143,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -147,22 +194,33 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
const userA = {
|
||||
name: 'd1',
|
||||
email: 'd.1@speckle.systems',
|
||||
password: 'wowwow8charsplease'
|
||||
password: 'wowwow8charsplease',
|
||||
id: '',
|
||||
token: ''
|
||||
}
|
||||
const userB = {
|
||||
name: 'd2',
|
||||
email: 'd.2@speckle.systems',
|
||||
password: 'wowwow8charsplease'
|
||||
password: 'wowwow8charsplease',
|
||||
id: '',
|
||||
token: ''
|
||||
}
|
||||
|
||||
const testStream = {
|
||||
name: 'Test Stream 01',
|
||||
description: 'wonderful test stream'
|
||||
description: 'wonderful test stream',
|
||||
id: '',
|
||||
ownerId: ''
|
||||
}
|
||||
|
||||
const privateTestStream = { name: 'Private Test Stream', isPublic: false }
|
||||
const privateTestStream = {
|
||||
name: 'Private Test Stream',
|
||||
isPublic: false,
|
||||
id: '',
|
||||
ownerId: ''
|
||||
}
|
||||
|
||||
let app
|
||||
let app: Express.Express
|
||||
before(async () => {
|
||||
;({ app } = await beforeEachContext())
|
||||
|
||||
@@ -277,7 +335,12 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
.post(`/objects/${testStream.id}`)
|
||||
.set('Authorization', userA.token)
|
||||
.set('Content-type', 'application/json')
|
||||
.attach(Buffer.from(JSON.stringify(objBatches[0]), 'utf8'))
|
||||
.attach(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Buffer.from(JSON.stringify(objBatches[0]), 'utf8') as any,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
undefined as any
|
||||
)
|
||||
expect(res).to.have.status(400)
|
||||
expect(res.text).to.equal(
|
||||
'Failed to parse request headers and body content as valid multipart/form-data.'
|
||||
@@ -289,7 +352,8 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
.post(`/objects/${testStream.id}`)
|
||||
.set('Authorization', userA.token)
|
||||
.set('Content-type', 'multipart/form-data')
|
||||
.attach(JSON.stringify(objBatches[0], 'utf8'))
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
.attach(JSON.stringify(objBatches[0]) as any, undefined as any)
|
||||
expect(res).to.have.status(400)
|
||||
expect(res.text).to.equal(
|
||||
'Failed to parse request headers and body content as valid multipart/form-data.'
|
||||
@@ -341,7 +405,8 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
it('Should not allow upload with invalid body (not contained within array)', async () => {
|
||||
//creating a single valid object
|
||||
const objectToPost = {
|
||||
name: 'yet again cannot believe i have to create this'
|
||||
name: 'yet again cannot believe i have to create this',
|
||||
id: ''
|
||||
}
|
||||
const objectId = crypto
|
||||
.createHash('md5')
|
||||
@@ -384,7 +449,7 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
// expect(res.text).contains('Object too large')
|
||||
// })
|
||||
|
||||
let parentId
|
||||
let parentId: string
|
||||
const numObjs = 5000
|
||||
const objBatches = [
|
||||
createManyObjects(numObjs),
|
||||
@@ -412,19 +477,22 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
})
|
||||
|
||||
it('Should properly download an object, with all its children, into a application/json response', (done) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
new Promise((resolve) => setTimeout(resolve, 1500)) // avoids race condition
|
||||
.then(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
request(app)
|
||||
.get(`/objects/${testStream.id}/${parentId}`)
|
||||
.set('Authorization', userA.token)
|
||||
.buffer()
|
||||
.parse((res, cb) => {
|
||||
res.data = ''
|
||||
res.on('data', (chunk) => {
|
||||
res.data += chunk.toString()
|
||||
const resTyped = res as typeof res & { data: string }
|
||||
resTyped.data = ''
|
||||
resTyped.on('data', (chunk) => {
|
||||
resTyped.data += chunk.toString()
|
||||
})
|
||||
res.on('end', () => {
|
||||
cb(null, res.data)
|
||||
resTyped.on('end', () => {
|
||||
cb(null, resTyped.data)
|
||||
})
|
||||
})
|
||||
.end((err, res) => {
|
||||
@@ -442,24 +510,27 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
})
|
||||
|
||||
it('Should properly download an object, with all its children, into a text/plain response', (done) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
request(app)
|
||||
.get(`/objects/${testStream.id}/${parentId}`)
|
||||
.set('Authorization', userA.token)
|
||||
.set('Accept', 'text/plain')
|
||||
.buffer()
|
||||
.parse((res, cb) => {
|
||||
res.data = ''
|
||||
res.on('data', (chunk) => {
|
||||
res.data += chunk.toString()
|
||||
const resTyped = res as typeof res & { data: string }
|
||||
|
||||
resTyped.data = ''
|
||||
resTyped.on('data', (chunk) => {
|
||||
resTyped.data += chunk.toString()
|
||||
})
|
||||
res.on('end', () => {
|
||||
cb(null, res.data)
|
||||
resTyped.on('end', () => {
|
||||
cb(null, resTyped.data)
|
||||
})
|
||||
})
|
||||
.end((err, res) => {
|
||||
if (err) done(err)
|
||||
try {
|
||||
const o = res.body.split('\n').filter((l) => l !== '')
|
||||
const o = res.body.split('\n').filter((l: string) => l !== '')
|
||||
expect(o.length).to.equal(numObjs + 1)
|
||||
expect(res).to.be.text
|
||||
done()
|
||||
@@ -474,6 +545,7 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
for (let i = 0; i < objBatches[0].length; i++) {
|
||||
objectIds.push(objBatches[0][i].id)
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
request(app)
|
||||
.post(`/api/getobjects/${testStream.id}`)
|
||||
.set('Authorization', userA.token)
|
||||
@@ -481,18 +553,20 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
.send({ objects: JSON.stringify(objectIds) })
|
||||
.buffer()
|
||||
.parse((res, cb) => {
|
||||
res.data = ''
|
||||
res.on('data', (chunk) => {
|
||||
res.data += chunk.toString()
|
||||
const resTyped = res as typeof res & { data: string }
|
||||
|
||||
resTyped.data = ''
|
||||
resTyped.on('data', (chunk) => {
|
||||
resTyped.data += chunk.toString()
|
||||
})
|
||||
res.on('end', () => {
|
||||
cb(null, res.data)
|
||||
resTyped.on('end', () => {
|
||||
cb(null, resTyped.data)
|
||||
})
|
||||
})
|
||||
.end((err, res) => {
|
||||
if (err) done(err)
|
||||
try {
|
||||
const o = res.body.split('\n').filter((l) => l !== '')
|
||||
const o = res.body.split('\n').filter((l: string) => l !== '')
|
||||
expect(o.length).to.equal(objectIds.length)
|
||||
expect(res).to.be.text
|
||||
done()
|
||||
@@ -530,7 +604,7 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
for (let i = 0; i < objBatches[0].length; i++) {
|
||||
objectIds.push(objBatches[0][i].id)
|
||||
}
|
||||
const fakeIds = []
|
||||
const fakeIds: string[] = []
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const fakeId = crypto
|
||||
.createHash('md5')
|
||||
@@ -540,18 +614,21 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
objectIds.push(fakeId)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
request(app)
|
||||
.post(`/api/diff/${testStream.id}`)
|
||||
.set('Authorization', userA.token)
|
||||
.send({ objects: JSON.stringify(objectIds) })
|
||||
.buffer()
|
||||
.parse((res, cb) => {
|
||||
res.data = ''
|
||||
res.on('data', (chunk) => {
|
||||
res.data += chunk.toString()
|
||||
const resTyped = res as typeof res & { data: string }
|
||||
|
||||
resTyped.data = ''
|
||||
resTyped.on('data', (chunk) => {
|
||||
resTyped.data += chunk.toString()
|
||||
})
|
||||
res.on('end', () => {
|
||||
cb(null, res.data)
|
||||
resTyped.on('end', () => {
|
||||
cb(null, resTyped.data)
|
||||
})
|
||||
})
|
||||
.end((err, res) => {
|
||||
@@ -590,7 +667,7 @@ describe('Upload/Download Routes @api-rest', () => {
|
||||
})
|
||||
|
||||
describe('Express @core-rest', () => {
|
||||
let app
|
||||
let app: Express.Express
|
||||
before(async () => {
|
||||
;({ app } = await beforeEachContext())
|
||||
})
|
||||
@@ -73,8 +73,12 @@ import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/pr
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
deleteAllResourceInvitesFactory,
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
@@ -94,6 +98,24 @@ import {
|
||||
import { changeUserRoleFactory } from '@/modules/core/services/users/management'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { createObjectFactory } from '@/modules/core/services/objects/management'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
@@ -129,6 +151,51 @@ const createCommitByBranchName = createCommitByBranchNameFactory({
|
||||
getBranchById: getBranchByIdFactory({ db })
|
||||
})
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -147,7 +214,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -54,7 +54,9 @@ import {
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory,
|
||||
deleteAllUserInvitesFactory
|
||||
deleteAllUserInvitesFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
@@ -95,7 +97,10 @@ import {
|
||||
changeUserRoleFactory
|
||||
} from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { dbLogger } from '@/observability/logging'
|
||||
import {
|
||||
storeApiTokenFactory,
|
||||
@@ -114,6 +119,15 @@ import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { getPaginatedBranchCommitsItemsByNameFactory } from '@/modules/core/services/commit/retrieval'
|
||||
import { getPaginatedStreamBranchesFactory } from '@/modules/core/services/branch/retrieval'
|
||||
import { createObjectFactory } from '@/modules/core/services/objects/management'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = legacyGetUserFactory({ db })
|
||||
@@ -141,6 +155,51 @@ const createCommitByBranchName = createCommitByBranchNameFactory({
|
||||
getBranchById: getBranchByIdFactory({ db })
|
||||
})
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -159,7 +218,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser: getUserFactory({ db }),
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -11,7 +11,8 @@ import { wait } from '@speckle/shared'
|
||||
import { createAuthedTestContext, ServerAndContext } from '@/test/graphqlHelper'
|
||||
import {
|
||||
createStreamFactory,
|
||||
getStreamFactory
|
||||
getStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
@@ -21,7 +22,9 @@ import {
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
@@ -48,8 +51,20 @@ import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
// To ensure that the invites are created in the correct order, we need to wait a bit between each creation
|
||||
const WAIT_TIMEOUT = 5
|
||||
@@ -58,6 +73,52 @@ const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -76,7 +137,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -264,7 +326,10 @@ describe('[Admin users list]', () => {
|
||||
userId
|
||||
},
|
||||
ownerId
|
||||
)
|
||||
).then((invite) => ({
|
||||
inviteId: invite.id,
|
||||
token: invite.token
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ import cryptoRandomString from 'crypto-random-string'
|
||||
import { noErrors } from '@/test/helpers'
|
||||
import {
|
||||
createStreamFactory,
|
||||
getStreamFactory
|
||||
getStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
@@ -21,7 +22,9 @@ import {
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
@@ -47,7 +50,10 @@ import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repos
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createTokenFactory } from '@/modules/core/services/tokens'
|
||||
import {
|
||||
@@ -57,11 +63,66 @@ import {
|
||||
} from '@/modules/core/repositories/tokens'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { TIME_MS } from '@speckle/shared'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -80,7 +141,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import {
|
||||
createStreamFactory,
|
||||
getStreamFactory
|
||||
getStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
@@ -11,7 +12,9 @@ import {
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
@@ -40,7 +43,10 @@ import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repos
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { manageFileImportExpiryFactory } from '@/modules/fileuploads/services/tasks'
|
||||
@@ -59,11 +65,66 @@ import { sleep } from '@/test/helpers'
|
||||
import { expect } from 'chai'
|
||||
import { FileUploadConvertedStatus } from '@/modules/fileuploads/helpers/types'
|
||||
import { TIME } from '@speckle/shared'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -82,7 +143,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -18,6 +18,11 @@ export type ServerInvitesEventsPayloads = {
|
||||
invite: ServerInviteRecord
|
||||
finalizerUserId: string
|
||||
accept: boolean
|
||||
/**
|
||||
* finalizerUserId will always be the invite target. This field will be the actual person triggering the action,
|
||||
* which in auto-accept flows will be the initial inviter. Use this for reporting.
|
||||
*/
|
||||
trueFinalizerUserId: string
|
||||
}
|
||||
[ServerInvitesEvents.Canceled]: {
|
||||
invite: ServerInviteRecord
|
||||
|
||||
@@ -35,6 +35,12 @@ export type PrimaryInviteResourceTarget<
|
||||
* If invite also has secondary resource targets, you can specify the expected roles here
|
||||
*/
|
||||
secondaryResourceRoles?: Partial<ResourceTargetTypeRoleTypeMap>
|
||||
|
||||
/**
|
||||
* Whether the invite should be auto accepted or not. If this is true, no invite is actually created or email sent,
|
||||
* and the accept process is done automatically without user involvement.
|
||||
*/
|
||||
autoAccept?: boolean
|
||||
}
|
||||
|
||||
export type ServerInviteResourceTarget = InviteResourceTarget<
|
||||
|
||||
@@ -110,6 +110,35 @@ const buildCollectAndValidateResourceTargets = () =>
|
||||
getStream
|
||||
})
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaborator
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const buildCreateAndSendServerOrProjectInvite = () =>
|
||||
createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
@@ -124,7 +153,8 @@ const buildCreateAndSendServerOrProjectInvite = () =>
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
})
|
||||
|
||||
export = {
|
||||
@@ -375,33 +405,7 @@ export = {
|
||||
streamId: projectId //legacy
|
||||
})
|
||||
const useProjectInvite = useProjectInviteAndNotifyFactory({
|
||||
finalizeInvite: finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaborator
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
})
|
||||
|
||||
await withOperationLogging(
|
||||
@@ -617,33 +621,7 @@ export = {
|
||||
async use(_parent, args, ctx) {
|
||||
const logger = ctx.log
|
||||
const useProjectInvite = useProjectInviteAndNotifyFactory({
|
||||
finalizeInvite: finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaborator
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
})
|
||||
|
||||
await withOperationLogging(
|
||||
|
||||
@@ -69,6 +69,10 @@ export const isProjectResourceTarget = (
|
||||
): target is ProjectInviteResourceTarget =>
|
||||
target.resourceType === ProjectInviteResourceType
|
||||
|
||||
export const isPrimaryResourceTarget = (
|
||||
target: InviteResourceTarget
|
||||
): target is PrimaryInviteResourceTarget => 'primary' in target && !!target.primary
|
||||
|
||||
export interface ResourceTargetTypeRoleTypeMap {
|
||||
[ServerInviteResourceType]: ServerRoles
|
||||
[ProjectInviteResourceType]: StreamRoles
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
BuildInviteEmailContents,
|
||||
CollectAndValidateResourceTargets,
|
||||
CreateAndSendInvite,
|
||||
FinalizeInvite,
|
||||
ResendInviteEmail
|
||||
} from '@/modules/serverinvites/services/operations'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
@@ -91,7 +92,8 @@ export const createAndSendInviteFactory =
|
||||
buildInviteEmailContents,
|
||||
emitEvent,
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite
|
||||
}: {
|
||||
findUserByTarget: FindUserByTarget
|
||||
insertInviteAndDeleteOld: InsertInviteAndDeleteOld
|
||||
@@ -100,6 +102,7 @@ export const createAndSendInviteFactory =
|
||||
emitEvent: EventBusEmit
|
||||
getUser: GetUser
|
||||
getServerInfo: GetServerInfo
|
||||
finalizeInvite: FinalizeInvite
|
||||
}): CreateAndSendInvite =>
|
||||
async (params, inviterResourceAccessLimits?) => {
|
||||
const sendInviteEmail = sendInviteEmailFactory({ buildInviteEmailContents })
|
||||
@@ -165,6 +168,19 @@ export const createAndSendInviteFactory =
|
||||
targetUser ? [targetUser.email, buildUserTarget(targetUser.id)!] : []
|
||||
)
|
||||
|
||||
const autoAccept = finalPrimaryResource.autoAccept
|
||||
if (autoAccept && targetUser?.id) {
|
||||
await finalizeInvite({
|
||||
finalizerUserId: targetUser.id,
|
||||
finalizerResourceAccessLimits: inviterResourceAccessLimits,
|
||||
accept: true,
|
||||
token: invite.token,
|
||||
resourceType: finalPrimaryResource.resourceType,
|
||||
trueFinalizerId: inviterId
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// generate and send email
|
||||
await sendInviteEmail({
|
||||
invite: finalInvite,
|
||||
@@ -180,11 +196,6 @@ export const createAndSendInviteFactory =
|
||||
invite: finalInvite
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
inviteId: invite.id,
|
||||
token: invite.token
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ export type InviteResult = {
|
||||
export type CreateAndSendInvite = (
|
||||
params: CreateInviteParams,
|
||||
inviterResourceAccessLimits: MaybeNullOrUndefined<TokenResourceIdentifier[]>
|
||||
) => Promise<InviteResult>
|
||||
) => Promise<void>
|
||||
|
||||
export type FinalizeInvite = (params: {
|
||||
finalizerUserId: string
|
||||
@@ -35,6 +35,11 @@ export type FinalizeInvite = (params: {
|
||||
* If the invite is accepted, the email will be attached to the user account as well in a verified state.
|
||||
*/
|
||||
allowAttachingNewEmail?: boolean
|
||||
/**
|
||||
* Allow someone else besides the target user to finalize the invite. Used in auto-accept flows. The finalizerUserId
|
||||
* must be the target of the invite, but this different one will be used in reporting/activityStream actions
|
||||
*/
|
||||
trueFinalizerId?: string
|
||||
}) => Promise<void>
|
||||
|
||||
export type ResendInviteEmail = (params: {
|
||||
@@ -80,6 +85,9 @@ export enum InviteFinalizationAction {
|
||||
*/
|
||||
export type ValidateResourceInviteBeforeFinalization = (params: {
|
||||
invite: ServerInviteRecord
|
||||
/**
|
||||
* Not necessarily the invite target, can also be the inviter in case of auto-accept
|
||||
*/
|
||||
finalizerUserId: string
|
||||
finalizerResourceAccessLimits: MaybeNullOrUndefined<TokenResourceIdentifier[]>
|
||||
action: InviteFinalizationAction
|
||||
|
||||
@@ -202,7 +202,8 @@ export const finalizeResourceInviteFactory =
|
||||
token,
|
||||
resourceType,
|
||||
finalizerResourceAccessLimits,
|
||||
allowAttachingNewEmail
|
||||
allowAttachingNewEmail,
|
||||
trueFinalizerId
|
||||
} = params
|
||||
|
||||
const finalizerUserTarget = buildUserTarget(finalizerUserId)
|
||||
@@ -318,7 +319,8 @@ export const finalizeResourceInviteFactory =
|
||||
payload: {
|
||||
invite,
|
||||
accept,
|
||||
finalizerUserId
|
||||
finalizerUserId,
|
||||
trueFinalizerUserId: trueFinalizerId || finalizerUserId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -490,14 +490,12 @@ describe('[Stream & Server Invites]', () => {
|
||||
})
|
||||
|
||||
// Creating some invites
|
||||
await Promise.all(
|
||||
invites.map((i) =>
|
||||
createInviteDirectly(i, me.id).then((o) => {
|
||||
i.inviteId = o.inviteId
|
||||
i.token = o.token
|
||||
})
|
||||
)
|
||||
)
|
||||
for (const invite of invites) {
|
||||
await createInviteDirectly(invite, me.id).then((o) => {
|
||||
invite.inviteId = o.id
|
||||
invite.token = o.token
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('they can resend pre-existing invites irregardless of type', async () => {
|
||||
@@ -568,14 +566,12 @@ describe('[Stream & Server Invites]', () => {
|
||||
}
|
||||
]
|
||||
|
||||
await Promise.all(
|
||||
deletableInvites.map((i) =>
|
||||
createInviteDirectly(i, me.id).then((o) => {
|
||||
i.inviteId = o.inviteId
|
||||
i.token = o.token
|
||||
})
|
||||
)
|
||||
)
|
||||
for (const deletableInvite of deletableInvites) {
|
||||
await createInviteDirectly(deletableInvite, me.id).then((o) => {
|
||||
deletableInvite.inviteId = o.id
|
||||
deletableInvite.token = o.token
|
||||
})
|
||||
}
|
||||
|
||||
// Delete all invites
|
||||
for (const invite of deletableInvites) {
|
||||
@@ -695,7 +691,7 @@ describe('[Stream & Server Invites]', () => {
|
||||
// Create an invite before each test so that we can mutate them
|
||||
// in each test as needed
|
||||
await createInviteDirectly(inviteFromOtherGuy, otherGuy.id).then((o) => {
|
||||
inviteFromOtherGuy.inviteId = o.inviteId
|
||||
inviteFromOtherGuy.inviteId = o.id
|
||||
inviteFromOtherGuy.token = o.token
|
||||
})
|
||||
})
|
||||
@@ -804,23 +800,21 @@ describe('[Stream & Server Invites]', () => {
|
||||
])
|
||||
|
||||
// Create a couple of static invites that shouldn't be mutated in tests
|
||||
await Promise.all([
|
||||
createInviteDirectly(myInvite, me.id).then((o) => {
|
||||
myInvite.inviteId = o.inviteId
|
||||
myInvite.token = o.token
|
||||
}),
|
||||
createInviteDirectly(otherGuysInvite, otherGuy.id).then((o) => {
|
||||
otherGuysInvite.inviteId = o.inviteId
|
||||
otherGuysInvite.token = o.token
|
||||
})
|
||||
])
|
||||
await createInviteDirectly(myInvite, me.id).then((o) => {
|
||||
myInvite.inviteId = o.id
|
||||
myInvite.token = o.token
|
||||
})
|
||||
await createInviteDirectly(otherGuysInvite, otherGuy.id).then((o) => {
|
||||
otherGuysInvite.inviteId = o.id
|
||||
otherGuysInvite.token = o.token
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create an invite before each test so that we can mutate them
|
||||
// in each test as needed
|
||||
await createInviteDirectly(dynamicInvite, me.id).then((o) => {
|
||||
dynamicInvite.inviteId = o.inviteId
|
||||
dynamicInvite.inviteId = o.id
|
||||
dynamicInvite.token = o.token
|
||||
})
|
||||
})
|
||||
@@ -895,24 +889,22 @@ describe('[Stream & Server Invites]', () => {
|
||||
await createTestUser(ownInvitesGuy)
|
||||
|
||||
// Invite him to a few streams
|
||||
await Promise.all([
|
||||
createInviteDirectly(
|
||||
{
|
||||
stream: myPrivateStream,
|
||||
// SPecifically w/ email
|
||||
email: ownInvitesGuy.email
|
||||
},
|
||||
me.id
|
||||
),
|
||||
createInviteDirectly(
|
||||
{
|
||||
// Specifically w/ id
|
||||
userId: ownInvitesGuy.id,
|
||||
stream: otherGuysStream
|
||||
},
|
||||
otherGuy.id
|
||||
)
|
||||
])
|
||||
await createInviteDirectly(
|
||||
{
|
||||
stream: myPrivateStream,
|
||||
// SPecifically w/ email
|
||||
email: ownInvitesGuy.email
|
||||
},
|
||||
me.id
|
||||
)
|
||||
await createInviteDirectly(
|
||||
{
|
||||
// Specifically w/ id
|
||||
userId: ownInvitesGuy.id,
|
||||
stream: otherGuysStream
|
||||
},
|
||||
otherGuy.id
|
||||
)
|
||||
|
||||
// Build authenticated apollo instance
|
||||
apollo = await testApolloServer({ authUserId: ownInvitesGuy.id })
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
import {
|
||||
createStreamFactory,
|
||||
getStreamFactory,
|
||||
grantStreamPermissionsFactory,
|
||||
markCommitStreamUpdatedFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
@@ -40,7 +41,9 @@ import {
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
@@ -66,7 +69,10 @@ import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { createPersonalAccessTokenFactory } from '@/modules/core/services/tokens'
|
||||
import {
|
||||
storeApiTokenFactory,
|
||||
@@ -76,9 +82,19 @@ import {
|
||||
} from '@/modules/core/repositories/tokens'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import { createObjectsFactory } from '@/modules/core/services/objects/management'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const markCommitStreamUpdated = markCommitStreamUpdatedFactory({ db })
|
||||
const getObject = getObjectFactory({ db })
|
||||
const createCommitByBranchId = createCommitByBranchIdFactory({
|
||||
@@ -99,6 +115,50 @@ const createCommitByBranchName = createCommitByBranchNameFactory({
|
||||
})
|
||||
|
||||
const getStream = getStreamFactory({ db })
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -117,7 +177,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser: getUserFactory({ db }),
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
@@ -124,6 +124,7 @@ export const dispatchStreamEventFactory =
|
||||
stream?: StreamWithOptionalRole
|
||||
userId?: string | null
|
||||
user?: Partial<UserWithOptionalRole> | null
|
||||
test?: string
|
||||
}
|
||||
}) => {
|
||||
const payload: typeof eventPayload & {
|
||||
|
||||
@@ -7,7 +7,8 @@ import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
createStreamFactory,
|
||||
getStreamFactory
|
||||
getStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
@@ -32,7 +33,9 @@ import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
@@ -40,13 +43,25 @@ import {
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import { expect } from 'chai'
|
||||
import crs from 'crypto-random-string'
|
||||
import { cleanOrphanedWebhookConfigsFactory } from '@/modules/webhooks/repositories/cleanup'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const WEBHOOKS_CONFIG_TABLE = 'webhooks_config'
|
||||
const WEBHOOKS_EVENTS_TABLE = 'webhooks_events'
|
||||
@@ -59,6 +74,52 @@ const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -77,7 +138,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
+133
-77
@@ -1,11 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
const expect = require('chai').expect
|
||||
const assert = require('assert')
|
||||
import { expect } from 'chai'
|
||||
import assert from 'assert'
|
||||
|
||||
const { beforeEachContext, initializeTestServer } = require('@/test/hooks')
|
||||
const { noErrors } = require('@/test/helpers')
|
||||
const { Scopes, Roles } = require('@speckle/shared')
|
||||
const {
|
||||
import { beforeEachContext, initializeTestServer } from '@/test/hooks'
|
||||
import { noErrors } from '@/test/helpers'
|
||||
import { Scopes, Roles, ensureError } from '@speckle/shared'
|
||||
import {
|
||||
createWebhookConfigFactory,
|
||||
countWebhooksByStreamIdFactory,
|
||||
getWebhookByIdFactory,
|
||||
@@ -14,78 +14,77 @@ const {
|
||||
getStreamWebhooksFactory,
|
||||
createWebhookEventFactory,
|
||||
getLastWebhookEventsFactory
|
||||
} = require('@/modules/webhooks/repositories/webhooks')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
} from '@/modules/webhooks/repositories/webhooks'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
createWebhookFactory,
|
||||
updateWebhookFactory,
|
||||
deleteWebhookFactory,
|
||||
dispatchStreamEventFactory
|
||||
} = require('@/modules/webhooks/services/webhooks')
|
||||
const {
|
||||
} from '@/modules/webhooks/services/webhooks'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const {
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const { createBranchFactory } = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
updateAllInviteTargetsFactory,
|
||||
findInviteFactory,
|
||||
deleteInvitesByTargetFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getUserFactory,
|
||||
getUsersFactory,
|
||||
storeUserFactory,
|
||||
countAdminUsersFactory,
|
||||
storeUserAclFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const {
|
||||
} from '@/modules/core/repositories/users'
|
||||
import {
|
||||
findEmailFactory,
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory
|
||||
} = require('@/modules/core/repositories/userEmails')
|
||||
const {
|
||||
requestNewEmailVerificationFactory
|
||||
} = require('@/modules/emails/services/verification/request')
|
||||
const {
|
||||
deleteOldAndInsertNewVerificationFactory
|
||||
} = require('@/modules/emails/repositories')
|
||||
const { renderEmail } = require('@/modules/emails/services/emailRendering')
|
||||
const { sendEmail } = require('@/modules/emails/services/sending')
|
||||
const { createUserFactory } = require('@/modules/core/services/users/management')
|
||||
const {
|
||||
validateAndCreateUserEmailFactory
|
||||
} = require('@/modules/core/services/userEmails')
|
||||
const {
|
||||
finalizeInvitedServerRegistrationFactory
|
||||
} = require('@/modules/serverinvites/services/processing')
|
||||
const { createPersonalAccessTokenFactory } = require('@/modules/core/services/tokens')
|
||||
const {
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { createUserFactory } from '@/modules/core/services/users/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { createPersonalAccessTokenFactory } from '@/modules/core/services/tokens'
|
||||
import {
|
||||
storeApiTokenFactory,
|
||||
storeTokenScopesFactory,
|
||||
storeTokenResourceAccessDefinitionsFactory,
|
||||
storePersonalApiTokenFactory
|
||||
} = require('@/modules/core/repositories/tokens')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
} from '@/modules/core/repositories/tokens'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import { omit } from 'lodash'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
@@ -95,6 +94,52 @@ const updateWebhook = updateWebhookFactory({
|
||||
updateWebhookConfig: updateWebhookConfigFactory({ db })
|
||||
})
|
||||
const getStreamWebhooks = getStreamWebhooksFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -113,7 +158,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
@@ -161,27 +207,32 @@ const createPersonalAccessToken = createPersonalAccessTokenFactory({
|
||||
|
||||
describe('Webhooks @webhooks', () => {
|
||||
const getWebhook = getWebhookByIdFactory({ db })
|
||||
let sendRequest
|
||||
let sendRequest: Awaited<ReturnType<typeof initializeTestServer>>['sendRequest']
|
||||
|
||||
const userOne = {
|
||||
name: 'User',
|
||||
email: 'user@example.org',
|
||||
password: 'jdsadjsadasfdsa'
|
||||
password: 'jdsadjsadasfdsa',
|
||||
id: '',
|
||||
token: ''
|
||||
}
|
||||
|
||||
const streamOne = {
|
||||
name: 'streamOne',
|
||||
description: 'stream',
|
||||
isPublic: true
|
||||
isPublic: true,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const webhookOne = {
|
||||
streamId: null, // filled in `before`
|
||||
streamId: '', // filled in `before`
|
||||
url: 'http://localhost:42/non-existent',
|
||||
description: 'test wh',
|
||||
secret: 'secret',
|
||||
enabled: true,
|
||||
triggers: ['commit_create', 'commit_update']
|
||||
triggers: ['commit_create', 'commit_update'],
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
@@ -209,7 +260,7 @@ describe('Webhooks @webhooks', () => {
|
||||
const webhook = await getWebhook({ id: webhookOne.id })
|
||||
expect(webhook).to.not.be.null
|
||||
expect(webhook).to.have.property('url')
|
||||
expect(webhook.url).to.equal(webhookOne.url)
|
||||
expect(webhook!.url).to.equal(webhookOne.url)
|
||||
})
|
||||
|
||||
it('Should update a webhook', async () => {
|
||||
@@ -223,7 +274,7 @@ describe('Webhooks @webhooks', () => {
|
||||
const webhook = await getWebhook({ id: webhookId })
|
||||
expect(webhook).to.not.be.null
|
||||
expect(webhook).to.have.property('url')
|
||||
expect(webhook.url).to.equal(newUrl)
|
||||
expect(webhook!.url).to.equal(newUrl)
|
||||
})
|
||||
|
||||
it('Should delete a webhook', async () => {
|
||||
@@ -240,7 +291,8 @@ describe('Webhooks @webhooks', () => {
|
||||
description: 'test wh',
|
||||
secret: 'secret',
|
||||
enabled: true,
|
||||
triggers: ['commit_create', 'commit_update']
|
||||
triggers: ['commit_create', 'commit_update'],
|
||||
id: ''
|
||||
}
|
||||
webhook.id = await createWebhookFactory({
|
||||
createWebhookConfig: createWebhookConfigFactory({ db }),
|
||||
@@ -307,7 +359,6 @@ describe('Webhooks @webhooks', () => {
|
||||
countWebhooksByStreamId: countWebhooksByStreamIdFactory({ db })
|
||||
})(webhook)
|
||||
await dispatchStreamEventFactory({
|
||||
db,
|
||||
getServerInfo,
|
||||
getStream,
|
||||
createWebhookEvent: createWebhookEventFactory({ db }),
|
||||
@@ -328,22 +379,27 @@ describe('Webhooks @webhooks', () => {
|
||||
const userTwo = {
|
||||
name: 'User2',
|
||||
email: 'user2@example.org',
|
||||
password: 'jdsadjsadasfdsa'
|
||||
password: 'jdsadjsadasfdsa',
|
||||
id: '',
|
||||
token: ''
|
||||
}
|
||||
|
||||
const webhookTwo = {
|
||||
streamId: null,
|
||||
streamId: '',
|
||||
url: 'http://localhost:42/non-existent-two',
|
||||
description: 'test wh no 2',
|
||||
secret: 'secret',
|
||||
enabled: true,
|
||||
triggers: ['commit_create', 'commit_update']
|
||||
triggers: ['commit_create', 'commit_update'],
|
||||
id: ''
|
||||
}
|
||||
|
||||
const streamTwo = {
|
||||
name: 'streamTwo',
|
||||
description: 'stream',
|
||||
isPublic: true
|
||||
isPublic: true,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
@@ -373,7 +429,7 @@ describe('Webhooks @webhooks', () => {
|
||||
const res = await sendRequest(userTwo.token, {
|
||||
query:
|
||||
'mutation createWebhook($webhook: WebhookCreateInput!) { webhookCreate( webhook: $webhook ) }',
|
||||
variables: { webhook: webhookTwo }
|
||||
variables: { webhook: omit(webhookTwo, ['id']) }
|
||||
})
|
||||
expect(noErrors(res))
|
||||
expect(res.body.data.webhookCreate).to.not.be.null
|
||||
@@ -382,7 +438,6 @@ describe('Webhooks @webhooks', () => {
|
||||
|
||||
it('Should get stream webhooks and the previous events', async () => {
|
||||
await dispatchStreamEventFactory({
|
||||
db,
|
||||
getServerInfo,
|
||||
getStream,
|
||||
getStreamWebhooks: getStreamWebhooksFactory({ db }),
|
||||
@@ -420,9 +475,9 @@ describe('Webhooks @webhooks', () => {
|
||||
})
|
||||
const webhook = await getWebhook({ id: webhookTwo.id })
|
||||
expect(noErrors(res))
|
||||
expect(res.body.data.webhookUpdate).to.equal(webhook.id)
|
||||
expect(webhook.description).to.equal('updated webhook')
|
||||
expect(webhook.enabled).to.equal(false)
|
||||
expect(res.body.data.webhookUpdate).to.equal(webhook!.id)
|
||||
expect(webhook!.description).to.equal('updated webhook')
|
||||
expect(webhook!.enabled).to.equal(false)
|
||||
})
|
||||
|
||||
it('Should *not* update or delete a webhook if the stream id and webhook id do not match', async () => {
|
||||
@@ -461,7 +516,8 @@ describe('Webhooks @webhooks', () => {
|
||||
description: 'test wh',
|
||||
secret: 'secret',
|
||||
enabled: true,
|
||||
triggers: ['commit_create', 'commit_update']
|
||||
triggers: ['commit_create', 'commit_update'],
|
||||
id: ''
|
||||
}
|
||||
webhook.id = await createWebhookFactory({
|
||||
createWebhookConfig: createWebhookConfigFactory({ db }),
|
||||
@@ -475,11 +531,11 @@ describe('Webhooks @webhooks', () => {
|
||||
})
|
||||
|
||||
it('Should *not* create a webhook if user is not a stream owner', async () => {
|
||||
delete webhookTwo.id
|
||||
webhookTwo.id = ''
|
||||
const res = await sendRequest(userOne.token, {
|
||||
query:
|
||||
'mutation createWebhook($webhook: WebhookCreateInput!) { webhookCreate( webhook: $webhook ) }',
|
||||
variables: { webhook: webhookTwo }
|
||||
variables: { webhook: omit(webhookTwo, ['id']) }
|
||||
})
|
||||
expect(res.body.errors).to.exist
|
||||
expect(res.body.errors[0].extensions.code).to.equal('FORBIDDEN')
|
||||
@@ -525,7 +581,7 @@ describe('Webhooks @webhooks', () => {
|
||||
countWebhooksByStreamId: countWebhooksByStreamIdFactory({ db })
|
||||
})(webhook)
|
||||
} catch (err) {
|
||||
if (err.toString().indexOf('Maximum') > -1) return
|
||||
if (ensureError(err).toString().indexOf('Maximum') > -1) return
|
||||
}
|
||||
|
||||
assert.fail('Configured more webhooks than the limit')
|
||||
@@ -217,6 +217,10 @@ import {
|
||||
throwIfAuthNotOk
|
||||
} from '@/modules/shared/helpers/errorHelper'
|
||||
import { withOperationLogging } from '@/observability/domain/businessLogging'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import { WorkspaceInvitesLimit } from '@/modules/workspaces/domain/constants'
|
||||
|
||||
const eventBus = getEventBus()
|
||||
@@ -255,6 +259,90 @@ const buildCollectAndValidateResourceTargets = () =>
|
||||
})
|
||||
})
|
||||
|
||||
const buildFinalizeWorkspaceInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({
|
||||
db,
|
||||
filterQuery: workspaceInviteValidityFilter
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
validateInvite: validateWorkspaceInviteBeforeFinalizationFactory({
|
||||
getWorkspace: getWorkspaceFactory({ db })
|
||||
}),
|
||||
processInvite: processFinalizedWorkspaceInviteFactory({
|
||||
getWorkspace: getWorkspaceFactory({ db }),
|
||||
updateWorkspaceRole: updateWorkspaceRoleFactory({
|
||||
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }),
|
||||
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({ db }),
|
||||
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
|
||||
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }),
|
||||
emitWorkspaceEvent: getEventBus().emit,
|
||||
ensureValidWorkspaceRoleSeat: ensureValidWorkspaceRoleSeatFactory({
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db }),
|
||||
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db }),
|
||||
eventEmit: getEventBus().emit
|
||||
})
|
||||
})
|
||||
}),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const validateStreamAccess = validateStreamAccessFactory({ authorizeResolver })
|
||||
const addOrUpdateStreamCollaborator = addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess,
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaborator
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const buildCreateAndSendServerOrProjectInvite = () =>
|
||||
createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
@@ -269,7 +357,8 @@ const buildCreateAndSendServerOrProjectInvite = () =>
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
})
|
||||
|
||||
const buildCreateAndSendWorkspaceInvite = () =>
|
||||
@@ -287,9 +376,9 @@ const buildCreateAndSendWorkspaceInvite = () =>
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeWorkspaceInvite()
|
||||
})
|
||||
const validateStreamAccess = validateStreamAccessFactory({ authorizeResolver })
|
||||
const isStreamCollaborator = isStreamCollaboratorFactory({
|
||||
getStream
|
||||
})
|
||||
@@ -1226,52 +1315,7 @@ export = FF_WORKSPACES_MODULE_ENABLED
|
||||
use: async (_parent, args, ctx) => {
|
||||
const logger = ctx.log
|
||||
|
||||
const finalizeInvite = finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({
|
||||
db,
|
||||
filterQuery: workspaceInviteValidityFilter
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
validateInvite: validateWorkspaceInviteBeforeFinalizationFactory({
|
||||
getWorkspace: getWorkspaceFactory({ db })
|
||||
}),
|
||||
processInvite: processFinalizedWorkspaceInviteFactory({
|
||||
getWorkspace: getWorkspaceFactory({ db }),
|
||||
updateWorkspaceRole: updateWorkspaceRoleFactory({
|
||||
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }),
|
||||
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({ db }),
|
||||
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
|
||||
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }),
|
||||
emitWorkspaceEvent: getEventBus().emit,
|
||||
ensureValidWorkspaceRoleSeat: ensureValidWorkspaceRoleSeatFactory({
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db }),
|
||||
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db }),
|
||||
eventEmit: getEventBus().emit
|
||||
})
|
||||
})
|
||||
}),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification
|
||||
}),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const finalizeInvite = buildFinalizeWorkspaceInvite()
|
||||
await withOperationLogging(
|
||||
async () =>
|
||||
await finalizeInvite({
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
} from '@/modules/serverinvites/errors'
|
||||
import {
|
||||
buildUserTarget,
|
||||
isPrimaryResourceTarget,
|
||||
isProjectResourceTarget,
|
||||
resolveInviteTargetTitle,
|
||||
resolveTarget
|
||||
@@ -225,6 +226,11 @@ export const collectAndValidateWorkspaceTargetsFactory =
|
||||
userId: targetUser.id,
|
||||
projectRole
|
||||
})
|
||||
|
||||
// If project target is primary and user target is already a workspace member, mark invite as auto-acceptable
|
||||
if (isPrimaryResourceTarget(projectTarget) && workspaceRole) {
|
||||
projectTarget.autoAccept = true
|
||||
}
|
||||
}
|
||||
|
||||
// Do further validation only if we're actually planning to invite to a workspace
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory,
|
||||
findEmailsByUserIdFactory,
|
||||
findVerifiedEmailsByUserIdFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
@@ -19,12 +26,15 @@ import {
|
||||
getWorkspaceDomainsFactory,
|
||||
storeWorkspaceDomainFactory,
|
||||
getWorkspaceBySlugFactory,
|
||||
getWorkspaceRoleForUserFactory
|
||||
getWorkspaceRoleForUserFactory,
|
||||
workspaceInviteValidityFilter
|
||||
} from '@/modules/workspaces/repositories/workspaces'
|
||||
import {
|
||||
buildWorkspaceInviteEmailContentsFactory,
|
||||
collectAndValidateWorkspaceTargetsFactory,
|
||||
createWorkspaceInviteFactory
|
||||
createWorkspaceInviteFactory,
|
||||
processFinalizedWorkspaceInviteFactory,
|
||||
validateWorkspaceInviteBeforeFinalizationFactory
|
||||
} from '@/modules/workspaces/services/invites'
|
||||
import {
|
||||
createWorkspaceFactory,
|
||||
@@ -92,6 +102,16 @@ import {
|
||||
getWorkspaceSeatTypeToProjectRoleMappingFactory,
|
||||
validateWorkspaceMemberProjectRoleFactory
|
||||
} from '@/modules/workspaces/services/projects'
|
||||
import { captureCreatedInvite } from '@/test/speckle-helpers/inviteHelper'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
|
||||
const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags()
|
||||
|
||||
@@ -378,10 +398,9 @@ export const createWorkspaceInviteDirectly = async (
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const createAndSendInvite = createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: collectAndValidateWorkspaceTargetsFactory({
|
||||
|
||||
const buildCollectAndValidateResourceTargets = () =>
|
||||
collectAndValidateWorkspaceTargetsFactory({
|
||||
getStream,
|
||||
getWorkspace: getWorkspaceFactory({ db }),
|
||||
getWorkspaceDomains: getWorkspaceDomainsFactory({ db }),
|
||||
@@ -400,7 +419,68 @@ export const createWorkspaceInviteDirectly = async (
|
||||
getWorkspaceWithPlan: getWorkspaceWithPlanFactory({ db })
|
||||
})
|
||||
})
|
||||
}),
|
||||
})
|
||||
|
||||
const buildFinalizeWorkspaceInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({
|
||||
db,
|
||||
filterQuery: workspaceInviteValidityFilter
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
validateInvite: validateWorkspaceInviteBeforeFinalizationFactory({
|
||||
getWorkspace: getWorkspaceFactory({ db })
|
||||
}),
|
||||
processInvite: processFinalizedWorkspaceInviteFactory({
|
||||
getWorkspace: getWorkspaceFactory({ db }),
|
||||
updateWorkspaceRole: updateWorkspaceRoleFactory({
|
||||
getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }),
|
||||
findVerifiedEmailsByUserId: findVerifiedEmailsByUserIdFactory({ db }),
|
||||
getWorkspaceRoles: getWorkspaceRolesFactory({ db }),
|
||||
upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }),
|
||||
emitWorkspaceEvent: getEventBus().emit,
|
||||
ensureValidWorkspaceRoleSeat: ensureValidWorkspaceRoleSeatFactory({
|
||||
createWorkspaceSeat: createWorkspaceSeatFactory({ db }),
|
||||
getWorkspaceUserSeat: getWorkspaceUserSeatFactory({ db }),
|
||||
eventEmit: getEventBus().emit
|
||||
})
|
||||
})
|
||||
}),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createAndSendInvite = createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: buildCollectAndValidateResourceTargets(),
|
||||
buildInviteEmailContents: buildWorkspaceInviteEmailContentsFactory({
|
||||
getStream,
|
||||
getWorkspace: getWorkspaceFactory({ db })
|
||||
@@ -411,18 +491,22 @@ export const createWorkspaceInviteDirectly = async (
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeWorkspaceInvite()
|
||||
})
|
||||
|
||||
const createInvite = createWorkspaceInviteFactory({
|
||||
createAndSendInvite
|
||||
})
|
||||
|
||||
return await createInvite({
|
||||
...args,
|
||||
inviterId,
|
||||
inviterResourceAccessRules: null
|
||||
})
|
||||
return await captureCreatedInvite(
|
||||
async () =>
|
||||
await createInvite({
|
||||
...args,
|
||||
inviterId,
|
||||
inviterResourceAccessRules: null
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
export const createTestOidcProvider = async (
|
||||
|
||||
@@ -559,7 +559,9 @@ describe('Workspaces Invites GQL', () => {
|
||||
Roles.Stream.Owner,
|
||||
me.id
|
||||
)
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
// Remove all project access from workspaceMemberWithNoProjectAccess
|
||||
await Promise.all([
|
||||
leaveStream(
|
||||
@@ -630,6 +632,38 @@ describe('Workspaces Invites GQL', () => {
|
||||
expect(res.data?.projectMutations.invites.createForWorkspace.id).to.not.be.ok
|
||||
})
|
||||
|
||||
it('can invite to workspace project as admin, even if target doesnt belong to workspace', async () => {
|
||||
const sendEmailInvocations = EmailSendingServiceMock.hijackFunction(
|
||||
'sendEmail',
|
||||
async () => true
|
||||
)
|
||||
|
||||
const res = await gqlHelpers.createWorkspaceProjectInvite({
|
||||
projectId: myProjectInviteTargetWorkspaceProject.id,
|
||||
inputs: [
|
||||
{
|
||||
userId: otherGuy.id,
|
||||
role: Roles.Stream.Reviewer
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.projectMutations.invites.createForWorkspace.id).to.be.ok
|
||||
|
||||
// no auto-accept, since target is not a workspace member
|
||||
expect(sendEmailInvocations.args).to.have.lengthOf(1)
|
||||
const emailParams = sendEmailInvocations.args[0][0]
|
||||
await validateInviteExistanceFromEmail(emailParams)
|
||||
|
||||
await gqlHelpers.validateResourceAccess({
|
||||
shouldHaveAccess: false,
|
||||
userId: otherGuy.id,
|
||||
workspaceId: myProjectInviteTargetWorkspace.id,
|
||||
streamId: myProjectInviteTargetWorkspaceProject.id
|
||||
})
|
||||
})
|
||||
|
||||
it('can invite to workspace project even if not workspace admin, if target already belongs to workspace', async () => {
|
||||
const res = await gqlHelpers.createWorkspaceProjectInvite(
|
||||
{
|
||||
@@ -652,6 +686,38 @@ describe('Workspaces Invites GQL', () => {
|
||||
expect(res.data?.projectMutations.invites.createForWorkspace.id).to.be.ok
|
||||
})
|
||||
|
||||
it('invite auto-accepted if both users already belong to the workspace', async () => {
|
||||
const sendEmailInvocations = EmailSendingServiceMock.hijackFunction(
|
||||
'sendEmail',
|
||||
async () => true
|
||||
)
|
||||
|
||||
const res = await gqlHelpers.createWorkspaceProjectInvite({
|
||||
projectId: myProjectInviteTargetWorkspaceProject.id,
|
||||
inputs: [
|
||||
{
|
||||
userId: workspaceMemberWithNoProjectAccess.id,
|
||||
role: Roles.Stream.Reviewer
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.projectMutations.invites.createForWorkspace.id).to.be.ok
|
||||
|
||||
// No invite email should be sent out, due to auto-accept
|
||||
expect(sendEmailInvocations.length()).to.eq(0)
|
||||
|
||||
// Should have project role
|
||||
await gqlHelpers.validateResourceAccess({
|
||||
shouldHaveAccess: true,
|
||||
userId: workspaceMemberWithNoProjectAccess.id,
|
||||
workspaceId: myProjectInviteTargetWorkspace.id,
|
||||
streamId: myProjectInviteTargetWorkspaceProject.id,
|
||||
expectedProjectRole: Roles.Stream.Reviewer
|
||||
})
|
||||
})
|
||||
|
||||
it("can't invite a workspace guest to be a workspace project owner", async () => {
|
||||
const res = await gqlHelpers.createWorkspaceProjectInvite({
|
||||
projectId: myProjectInviteTargetWorkspaceProject.id,
|
||||
@@ -1097,7 +1163,7 @@ describe('Workspaces Invites GQL', () => {
|
||||
},
|
||||
me.id
|
||||
)
|
||||
expect(brokenInvite.inviteId).to.be.ok
|
||||
expect(brokenInvite.id).to.be.ok
|
||||
|
||||
// Db query directly, cause this isn't a supported use case
|
||||
await Workspaces.knex()
|
||||
@@ -1569,8 +1635,7 @@ describe('Workspaces Invites GQL', () => {
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.workspaceMutations.invites.use).to.be.ok
|
||||
expect(await findInviteFactory({ db })({ inviteId: invite.inviteId })).to.be.not
|
||||
.ok
|
||||
expect(await findInviteFactory({ db })({ inviteId: invite.id })).to.be.not.ok
|
||||
|
||||
await gqlHelpers.validateResourceAccess({
|
||||
shouldHaveAccess: true,
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
require('../bootstrap')
|
||||
const { createManyObjects } = require('@/test/helpers')
|
||||
const { fetch } = require('undici')
|
||||
const { init } = require(`@/app`)
|
||||
const request = require('supertest')
|
||||
const { exit } = require('yargs')
|
||||
const { logger } = require('@/observability/logging')
|
||||
const { Scopes } = require('@speckle/shared')
|
||||
const {
|
||||
getStreamFactory,
|
||||
createStreamFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} = require('@/modules/core/services/streams/management')
|
||||
const {
|
||||
inviteUsersToProjectFactory
|
||||
} = require('@/modules/serverinvites/services/projectInviteManagement')
|
||||
const {
|
||||
createAndSendInviteFactory
|
||||
} = require('@/modules/serverinvites/services/creation')
|
||||
const {
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory
|
||||
} = require('@/modules/serverinvites/repositories/serverInvites')
|
||||
const {
|
||||
collectAndValidateCoreTargetsFactory
|
||||
} = require('@/modules/serverinvites/services/coreResourceCollection')
|
||||
const {
|
||||
buildCoreInviteEmailContentsFactory
|
||||
} = require('@/modules/serverinvites/services/coreEmailContents')
|
||||
const { getEventBus } = require('@/modules/shared/services/eventBus')
|
||||
const { createBranchFactory } = require('@/modules/core/repositories/branches')
|
||||
const {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
legacyGetUserByEmailFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const { createPersonalAccessTokenFactory } = require('@/modules/core/services/tokens')
|
||||
const {
|
||||
storeApiTokenFactory,
|
||||
storeTokenScopesFactory,
|
||||
storeTokenResourceAccessDefinitionsFactory,
|
||||
storePersonalApiTokenFactory
|
||||
} = require('@/modules/core/repositories/tokens')
|
||||
const { getServerInfoFactory } = require('@/modules/core/repositories/server')
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
buildInviteEmailContents: buildCoreInviteEmailContentsFactory({
|
||||
getStream
|
||||
}),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
createStream: createStreamFactory({ db }),
|
||||
createBranch: createBranchFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
})
|
||||
const getUserByEmail = legacyGetUserByEmailFactory({ db })
|
||||
const createPersonalAccessToken = createPersonalAccessTokenFactory({
|
||||
storeApiToken: storeApiTokenFactory({ db }),
|
||||
storeTokenScopes: storeTokenScopesFactory({ db }),
|
||||
storeTokenResourceAccessDefinitions: storeTokenResourceAccessDefinitionsFactory({
|
||||
db
|
||||
}),
|
||||
storePersonalApiToken: storePersonalApiTokenFactory({ db })
|
||||
})
|
||||
|
||||
const main = async () => {
|
||||
const testStream = {
|
||||
name: 'Test Stream 01',
|
||||
description: 'wonderful test stream'
|
||||
}
|
||||
|
||||
// const userA = {
|
||||
// name: 'd1',
|
||||
// email: 'd.1@speckle.systems',
|
||||
// password: 'wowwow8charsplease'
|
||||
// }
|
||||
// userA.id = await createUser(userA)
|
||||
|
||||
const userA = await getUserByEmail({
|
||||
email: 'd.1@speckle.systems'
|
||||
})
|
||||
userA.token = `Bearer ${await createPersonalAccessToken(
|
||||
userA.id,
|
||||
'test token user A',
|
||||
[
|
||||
Scopes.Streams.Read,
|
||||
Scopes.Streams.Write,
|
||||
Scopes.Users.Read,
|
||||
Scopes.Users.Email,
|
||||
Scopes.Tokens.Write,
|
||||
Scopes.Tokens.Read,
|
||||
Scopes.Profile.Read,
|
||||
Scopes.Profile.Email
|
||||
]
|
||||
)}`
|
||||
|
||||
testStream.id = await createStream({ ...testStream, ownerId: userA.id })
|
||||
|
||||
const { app } = await init()
|
||||
|
||||
const numObjs = 5000
|
||||
const objBatch = createManyObjects(numObjs)
|
||||
|
||||
const uploadRes = await request(app)
|
||||
.post(`/objects/${testStream.id}`)
|
||||
.set('Authorization', userA.token)
|
||||
.set('Content-type', 'multipart/form-data')
|
||||
.attach('batch1', Buffer.from(JSON.stringify(objBatch), 'utf8'))
|
||||
|
||||
logger.info(uploadRes.status)
|
||||
const objectIds = objBatch.map((obj) => obj.id)
|
||||
|
||||
const res = await fetch(`http://127.0.0.1:3000/api/getobjects/${testStream.id}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: userA.token,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'text/plain'
|
||||
},
|
||||
body: JSON.stringify({ objects: JSON.stringify(objectIds) })
|
||||
})
|
||||
const data = await res.body.getReader().read()
|
||||
logger.info(data)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
main().then(logger.info('created')).catch(logger.error('failed'))
|
||||
@@ -0,0 +1,226 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import '../bootstrap'
|
||||
import { createManyObjects } from '@/test/helpers'
|
||||
import { fetch } from 'undici'
|
||||
import { init } from '@/app'
|
||||
import request from 'supertest'
|
||||
import { logger } from '@/observability/logging'
|
||||
import { Scopes } from '@speckle/shared'
|
||||
import {
|
||||
getStreamFactory,
|
||||
createStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
legacyCreateStreamFactory,
|
||||
createStreamReturnRecordFactory
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { createBranchFactory } from '@/modules/core/repositories/branches'
|
||||
import {
|
||||
getUsersFactory,
|
||||
getUserFactory,
|
||||
legacyGetUserByEmailFactory
|
||||
} from '@/modules/core/repositories/users'
|
||||
import { createPersonalAccessTokenFactory } from '@/modules/core/services/tokens'
|
||||
import {
|
||||
storeApiTokenFactory,
|
||||
storeTokenScopesFactory,
|
||||
storeTokenResourceAccessDefinitionsFactory,
|
||||
storePersonalApiTokenFactory
|
||||
} from '@/modules/core/repositories/tokens'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
buildInviteEmailContents: buildCoreInviteEmailContentsFactory({
|
||||
getStream
|
||||
}),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
createStream: createStreamFactory({ db }),
|
||||
createBranch: createBranchFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
})
|
||||
const getUserByEmail = legacyGetUserByEmailFactory({ db })
|
||||
const createPersonalAccessToken = createPersonalAccessTokenFactory({
|
||||
storeApiToken: storeApiTokenFactory({ db }),
|
||||
storeTokenScopes: storeTokenScopesFactory({ db }),
|
||||
storeTokenResourceAccessDefinitions: storeTokenResourceAccessDefinitionsFactory({
|
||||
db
|
||||
}),
|
||||
storePersonalApiToken: storePersonalApiTokenFactory({ db })
|
||||
})
|
||||
|
||||
const main = async () => {
|
||||
const testStream = {
|
||||
name: 'Test Stream 01',
|
||||
description: 'wonderful test stream',
|
||||
id: ''
|
||||
}
|
||||
|
||||
// const userA = {
|
||||
// name: 'd1',
|
||||
// email: 'd.1@speckle.systems',
|
||||
// password: 'wowwow8charsplease'
|
||||
// }
|
||||
// userA.id = await createUser(userA)
|
||||
|
||||
const userA = {
|
||||
...(await getUserByEmail({
|
||||
email: 'd.1@speckle.systems'
|
||||
}))!,
|
||||
token: ''
|
||||
}
|
||||
|
||||
userA.token = `Bearer ${await createPersonalAccessToken(
|
||||
userA.id,
|
||||
'test token user A',
|
||||
[
|
||||
Scopes.Streams.Read,
|
||||
Scopes.Streams.Write,
|
||||
Scopes.Users.Read,
|
||||
Scopes.Users.Email,
|
||||
Scopes.Tokens.Write,
|
||||
Scopes.Tokens.Read,
|
||||
Scopes.Profile.Read,
|
||||
Scopes.Profile.Email
|
||||
]
|
||||
)}`
|
||||
|
||||
testStream.id = await createStream({ ...testStream, ownerId: userA.id })
|
||||
|
||||
const { app } = await init()
|
||||
|
||||
const numObjs = 5000
|
||||
const objBatch = createManyObjects(numObjs)
|
||||
|
||||
const uploadRes = await request(app)
|
||||
.post(`/objects/${testStream.id}`)
|
||||
.set('Authorization', userA.token)
|
||||
.set('Content-type', 'multipart/form-data')
|
||||
.attach('batch1', Buffer.from(JSON.stringify(objBatch), 'utf8'))
|
||||
|
||||
logger.info(uploadRes.status)
|
||||
const objectIds = objBatch.map((obj) => obj.id)
|
||||
|
||||
const res = await fetch(`http://127.0.0.1:3000/api/getobjects/${testStream.id}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: userA.token,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'text/plain'
|
||||
},
|
||||
body: JSON.stringify({ objects: JSON.stringify(objectIds) })
|
||||
})
|
||||
const data = await res.body!.getReader().read()
|
||||
logger.info(data)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => logger.info('created'))
|
||||
.catch((err) => logger.error('failed', err))
|
||||
@@ -1,11 +1,14 @@
|
||||
import { MaybeAsync, Roles, StreamRoles } from '@speckle/shared'
|
||||
|
||||
import { buildUserTarget } from '@/modules/serverinvites/helpers/core'
|
||||
import { InviteResult } from '@/modules/serverinvites/services/operations'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteByTokenFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import { BasicTestUser } from '@/test/authHelper'
|
||||
@@ -17,21 +20,93 @@ import {
|
||||
ProjectInviteResourceType,
|
||||
ServerInviteResourceType
|
||||
} from '@/modules/serverinvites/domain/constants'
|
||||
import { SendEmailParams } from '@/modules/emails/services/sending'
|
||||
import { sendEmail, SendEmailParams } from '@/modules/emails/services/sending'
|
||||
import { db } from '@/db/knex'
|
||||
import { expect } from 'chai'
|
||||
import {
|
||||
PrimaryInviteResourceTarget,
|
||||
ServerInviteRecord,
|
||||
ServerInviteResourceTarget
|
||||
} from '@/modules/serverinvites/domain/types'
|
||||
import { EmailSendingServiceMock } from '@/test/mocks/global'
|
||||
import { getStreamFactory } from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
getStreamFactory,
|
||||
grantStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import { getUserFactory } from '@/modules/core/repositories/users'
|
||||
import { getServerInfoFactory } from '@/modules/core/repositories/server'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createAndSendInvite = createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
@@ -47,7 +122,8 @@ const createAndSendInvite = createAndSendInviteFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
})
|
||||
|
||||
export const createServerInviteDirectly = async (
|
||||
@@ -89,7 +165,7 @@ export const createStreamInviteDirectly = async (
|
||||
role?: StreamRoles
|
||||
},
|
||||
creatorId: string
|
||||
): Promise<InviteResult> => {
|
||||
): Promise<ServerInviteRecord> => {
|
||||
const userId = invite.userId || invite.user?.id || null
|
||||
const email = invite.email || null
|
||||
if (!userId && !email) throw new Error('Either user/userId or email must be set')
|
||||
@@ -100,19 +176,24 @@ export const createStreamInviteDirectly = async (
|
||||
const target = email || buildUserTarget(userId!)
|
||||
if (!target) throw new Error('Cannot create invite without a target')
|
||||
|
||||
return await createAndSendInvite(
|
||||
{
|
||||
target,
|
||||
inviterId: creatorId,
|
||||
message: invite.message,
|
||||
primaryResourceTarget: {
|
||||
resourceType: streamId ? ProjectInviteResourceType : ServerInviteResourceType,
|
||||
resourceId: streamId || '',
|
||||
role: streamId ? streamRole : Roles.Server.User,
|
||||
primary: true
|
||||
}
|
||||
},
|
||||
null
|
||||
return await captureCreatedInvite(
|
||||
async () =>
|
||||
await createAndSendInvite(
|
||||
{
|
||||
target,
|
||||
inviterId: creatorId,
|
||||
message: invite.message,
|
||||
primaryResourceTarget: {
|
||||
resourceType: streamId
|
||||
? ProjectInviteResourceType
|
||||
: ServerInviteResourceType,
|
||||
resourceId: streamId || '',
|
||||
role: streamId ? streamRole : Roles.Server.User,
|
||||
primary: true
|
||||
}
|
||||
},
|
||||
null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ import {
|
||||
grantStreamPermissionsFactory,
|
||||
revokeStreamPermissionsFactory
|
||||
} from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
createUserEmailFactory,
|
||||
ensureNoPrimaryEmailForUserFactory,
|
||||
findEmailFactory
|
||||
} from '@/modules/core/repositories/userEmails'
|
||||
import { getUserFactory, getUsersFactory } from '@/modules/core/repositories/users'
|
||||
import {
|
||||
addOrUpdateStreamCollaboratorFactory,
|
||||
@@ -21,13 +26,30 @@ import {
|
||||
createStreamReturnRecordFactory,
|
||||
legacyCreateStreamFactory
|
||||
} from '@/modules/core/services/streams/management'
|
||||
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
|
||||
import { deleteOldAndInsertNewVerificationFactory } from '@/modules/emails/repositories'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
import { sendEmail } from '@/modules/emails/services/sending'
|
||||
import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request'
|
||||
import {
|
||||
deleteInvitesByTargetFactory,
|
||||
deleteServerOnlyInvitesFactory,
|
||||
findInviteFactory,
|
||||
findUserByTargetFactory,
|
||||
insertInviteAndDeleteOldFactory
|
||||
insertInviteAndDeleteOldFactory,
|
||||
updateAllInviteTargetsFactory
|
||||
} from '@/modules/serverinvites/repositories/serverInvites'
|
||||
import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
} from '@/modules/serverinvites/services/coreFinalization'
|
||||
import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection'
|
||||
import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation'
|
||||
import {
|
||||
finalizeInvitedServerRegistrationFactory,
|
||||
finalizeResourceInviteFactory
|
||||
} from '@/modules/serverinvites/services/processing'
|
||||
import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
@@ -44,6 +66,52 @@ const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUsers = getUsersFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
|
||||
const buildFinalizeProjectInvite = () =>
|
||||
finalizeResourceInviteFactory({
|
||||
findInvite: findInviteFactory({ db }),
|
||||
validateInvite: validateProjectInviteBeforeFinalizationFactory({
|
||||
getProject: getStream
|
||||
}),
|
||||
processInvite: processFinalizedProjectInviteFactory({
|
||||
getProject: getStream,
|
||||
addProjectRole: addOrUpdateStreamCollaboratorFactory({
|
||||
validateStreamAccess: validateStreamAccessFactory({ authorizeResolver }),
|
||||
getUser,
|
||||
grantStreamPermissions: grantStreamPermissionsFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
}),
|
||||
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
emitEvent: (...args) => getEventBus().emit(...args),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
validateAndCreateUserEmail: validateAndCreateUserEmailFactory({
|
||||
createUserEmail: createUserEmailFactory({ db }),
|
||||
ensureNoPrimaryEmailForUser: ensureNoPrimaryEmailForUserFactory({ db }),
|
||||
findEmail: findEmailFactory({ db }),
|
||||
updateEmailInvites: finalizeInvitedServerRegistrationFactory({
|
||||
deleteServerOnlyInvites: deleteServerOnlyInvitesFactory({ db }),
|
||||
updateAllInviteTargets: updateAllInviteTargetsFactory({ db })
|
||||
}),
|
||||
requestNewEmailVerification: requestNewEmailVerificationFactory({
|
||||
findEmail: findEmailFactory({ db }),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({
|
||||
db
|
||||
}),
|
||||
renderEmail,
|
||||
sendEmail
|
||||
})
|
||||
}),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
@@ -62,7 +130,8 @@ const createStream = legacyCreateStreamFactory({
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
getUsers
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user