feat: "workspace" project visibility (#4704)
* WIP new visi * test fixes * visibility seems to work * authz policies & authorizeResolver updated * various test fixes * users tests * frontend changes * minor adjustments * shared test fix * test fixes * force rerun CI
This commit is contained in:
committed by
GitHub
parent
02b97bcb86
commit
4db1531064
@@ -44,7 +44,8 @@ import {
|
||||
ServerAclRecord,
|
||||
BranchRecord,
|
||||
StreamAclRecord,
|
||||
StreamRecord
|
||||
StreamRecord,
|
||||
ProjectRecordVisibility
|
||||
} from '@/modules/core/helpers/types'
|
||||
import { WorkspaceInvalidRoleError } from '@/modules/workspaces/errors/workspace'
|
||||
import {
|
||||
@@ -564,8 +565,11 @@ const getPaginatedWorkspaceProjectsBaseQueryFactory =
|
||||
/**
|
||||
* If userId is set:
|
||||
* - If no workspace role, user should be server admin w/ admin override enabled
|
||||
* - If workspace role is guest, user should have explicit stream roles
|
||||
* - If workspace role other than guest, just get all workspace streams
|
||||
* - If workspace role is admin: user can get all workspace streams
|
||||
* - If workspace role is guest: user should have explicit stream roles
|
||||
* - If workspace role is member:
|
||||
* - Public/Workspace visibility: get stream
|
||||
* - Private visibility: user should have explicit stream roles
|
||||
*
|
||||
* If withProjectRoleOnly is set: Require project role always
|
||||
*/
|
||||
@@ -590,10 +594,21 @@ const getPaginatedWorkspaceProjectsBaseQueryFactory =
|
||||
}
|
||||
|
||||
w.orWhere((w2) => {
|
||||
// Ensure workspace role exists and its not guest or the user has explicit stream roles
|
||||
// Ensure workspace role exists and:
|
||||
// user has explicit stream role or is admin or is a non-guest in a non-private project
|
||||
w2.whereNotNull(DbWorkspaceAcl.col.role).andWhere((w3) => {
|
||||
if (!withProjectRoleOnly) {
|
||||
w3.whereNot(DbWorkspaceAcl.col.role, Roles.Workspace.Guest)
|
||||
w3.where(DbWorkspaceAcl.col.role, Roles.Workspace.Admin).orWhere(
|
||||
(w4) => {
|
||||
w4.whereNot(
|
||||
DbWorkspaceAcl.col.role,
|
||||
Roles.Workspace.Guest
|
||||
).andWhereNot(
|
||||
Streams.col.visibility,
|
||||
ProjectRecordVisibility.Private
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
w3.orWhereExists(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StreamRecord } from '@/modules/core/helpers/types'
|
||||
import { ProjectRecordVisibility, StreamRecord } from '@/modules/core/helpers/types'
|
||||
import {
|
||||
GetDefaultRegion,
|
||||
GetWorkspaceDomains,
|
||||
@@ -211,7 +211,17 @@ export const moveProjectToWorkspaceFactory =
|
||||
}
|
||||
|
||||
// Assign project to workspace
|
||||
return await updateProject({ projectUpdate: { id: projectId, workspaceId } })
|
||||
return await updateProject({
|
||||
projectUpdate: {
|
||||
id: projectId,
|
||||
workspaceId,
|
||||
visibility:
|
||||
// Migrate from Private -> Workspace visibility
|
||||
project.visibility === ProjectRecordVisibility.Private
|
||||
? ProjectRecordVisibility.Workspace
|
||||
: project.visibility
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const getWorkspaceRoleToDefaultProjectRoleMappingFactory =
|
||||
|
||||
@@ -32,6 +32,7 @@ import { expect } from 'chai'
|
||||
import { MaybeAsync, StreamRoles, WorkspaceRoles } from '@speckle/shared'
|
||||
import { expectToThrow } from '@/test/assertionHelper'
|
||||
import { ForbiddenError } from '@/modules/shared/errors'
|
||||
import { isBoolean } from 'lodash'
|
||||
|
||||
export const buildInvitesGraphqlOperations = (deps: { apollo: TestApolloServer }) => {
|
||||
const { apollo } = deps
|
||||
@@ -80,7 +81,7 @@ export const buildInvitesGraphqlOperations = (deps: { apollo: TestApolloServer }
|
||||
) => apollo.execute(UseWorkspaceProjectInviteDocument, args, options)
|
||||
|
||||
const validateResourceAccess = async (params: {
|
||||
shouldHaveAccess: boolean
|
||||
shouldHaveAccess: boolean | { workspace: boolean; project: boolean }
|
||||
userId: string
|
||||
workspaceId: string
|
||||
streamId?: string
|
||||
@@ -88,8 +89,17 @@ export const buildInvitesGraphqlOperations = (deps: { apollo: TestApolloServer }
|
||||
expectedProjectRole?: StreamRoles
|
||||
}) => {
|
||||
const { shouldHaveAccess, userId, workspaceId, streamId } = params
|
||||
const shouldHaveWorkspaceAccess = isBoolean(shouldHaveAccess)
|
||||
? shouldHaveAccess
|
||||
: shouldHaveAccess.workspace
|
||||
const shouldHaveProjectAccess = isBoolean(shouldHaveAccess)
|
||||
? shouldHaveAccess
|
||||
: shouldHaveAccess.project
|
||||
|
||||
const wrapAccessCheck = async (fn: () => MaybeAsync<unknown>) => {
|
||||
const wrapAccessCheck = async (
|
||||
fn: () => MaybeAsync<unknown>,
|
||||
shouldHaveAccess: boolean
|
||||
) => {
|
||||
if (shouldHaveAccess) {
|
||||
await fn()
|
||||
} else {
|
||||
@@ -113,7 +123,7 @@ export const buildInvitesGraphqlOperations = (deps: { apollo: TestApolloServer }
|
||||
`Unexpected workspace role! Expected: ${params.expectedWorkspaceRole}, real: ${workspace.role}`
|
||||
)
|
||||
}
|
||||
})
|
||||
}, shouldHaveWorkspaceAccess)
|
||||
|
||||
if (streamId?.length) {
|
||||
await wrapAccessCheck(async () => {
|
||||
@@ -133,7 +143,7 @@ export const buildInvitesGraphqlOperations = (deps: { apollo: TestApolloServer }
|
||||
`Unexpected project role! Expected: ${params.expectedProjectRole}, real: ${project?.role}`
|
||||
)
|
||||
}
|
||||
})
|
||||
}, shouldHaveProjectAccess)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
} from '@/modules/workspaces/tests/helpers/invites'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { WorkspaceSeatType } from '@/modules/workspacesCore/domain/types'
|
||||
import { ProjectRecordVisibility } from '@/modules/core/helpers/types'
|
||||
|
||||
enum InviteByTarget {
|
||||
Email = 'email',
|
||||
@@ -1017,7 +1018,14 @@ describe('Workspaces Invites GQL', () => {
|
||||
name: 'My Invite Target Workspace Stream 1',
|
||||
id: '',
|
||||
ownerId: '',
|
||||
isPublic: false
|
||||
visibility: ProjectRecordVisibility.Workspace
|
||||
}
|
||||
|
||||
const myInviteTargetPrivateWorkspaceStream1: BasicTestStream = {
|
||||
name: 'My Invite Target Private Workspace Stream 1',
|
||||
id: '',
|
||||
ownerId: '',
|
||||
visibility: ProjectRecordVisibility.Private
|
||||
}
|
||||
|
||||
const processableWorkspaceInvite = {
|
||||
@@ -1050,15 +1058,16 @@ describe('Workspaces Invites GQL', () => {
|
||||
}
|
||||
|
||||
const validateResourceAccess = async (params: {
|
||||
shouldHaveAccess: boolean
|
||||
shouldHaveAccess: boolean | { workspace: boolean; project: boolean }
|
||||
expectedWorkspaceRole?: WorkspaceRoles
|
||||
expectedProjectRole?: StreamRoles
|
||||
streamId: string
|
||||
}) => {
|
||||
return gqlHelpers.validateResourceAccess({
|
||||
...params,
|
||||
userId: otherGuy.id,
|
||||
workspaceId: myInviteTargetWorkspace.id,
|
||||
streamId: myInviteTargetWorkspaceStream1.id
|
||||
streamId: params.streamId
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1067,7 +1076,11 @@ describe('Workspaces Invites GQL', () => {
|
||||
await createTestWorkspaces([[myInviteTargetWorkspace, me]])
|
||||
|
||||
myInviteTargetWorkspaceStream1.workspaceId = myInviteTargetWorkspace.id
|
||||
await createTestStreams([[myInviteTargetWorkspaceStream1, me]])
|
||||
myInviteTargetPrivateWorkspaceStream1.workspaceId = myInviteTargetWorkspace.id
|
||||
await createTestStreams([
|
||||
[myInviteTargetWorkspaceStream1, me],
|
||||
[myInviteTargetPrivateWorkspaceStream1, me]
|
||||
])
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -1392,7 +1405,15 @@ describe('Workspaces Invites GQL', () => {
|
||||
})
|
||||
expect(invite).to.be.not.ok
|
||||
|
||||
await validateResourceAccess({ shouldHaveAccess: accept })
|
||||
// Should have access to workspace visibility stream, not the other one
|
||||
await validateResourceAccess({
|
||||
shouldHaveAccess: accept,
|
||||
streamId: myInviteTargetWorkspaceStream1.id
|
||||
})
|
||||
await validateResourceAccess({
|
||||
shouldHaveAccess: { workspace: accept, project: false },
|
||||
streamId: myInviteTargetPrivateWorkspaceStream1.id
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1440,7 +1461,10 @@ describe('Workspaces Invites GQL', () => {
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.workspaceMutations.invites.use).to.be.ok
|
||||
|
||||
await validateResourceAccess({ shouldHaveAccess: accept })
|
||||
await validateResourceAccess({
|
||||
shouldHaveAccess: accept,
|
||||
streamId: myInviteTargetWorkspaceStream1.id
|
||||
})
|
||||
|
||||
const verifiedEmails = await findVerifiedEmailsByUserIdFactory({
|
||||
db
|
||||
@@ -1487,7 +1511,8 @@ describe('Workspaces Invites GQL', () => {
|
||||
|
||||
await validateResourceAccess({
|
||||
shouldHaveAccess: true,
|
||||
expectedWorkspaceRole: Roles.Workspace.Member
|
||||
expectedWorkspaceRole: Roles.Workspace.Member,
|
||||
streamId: myInviteTargetWorkspaceStream1.id
|
||||
})
|
||||
|
||||
const targetInvite = roleChanged
|
||||
@@ -1516,7 +1541,8 @@ describe('Workspaces Invites GQL', () => {
|
||||
shouldHaveAccess: true,
|
||||
expectedWorkspaceRole: roleChanged
|
||||
? Roles.Workspace.Admin
|
||||
: Roles.Workspace.Member
|
||||
: Roles.Workspace.Member,
|
||||
streamId: myInviteTargetWorkspaceStream1.id
|
||||
})
|
||||
|
||||
const email = targetInvite.email
|
||||
@@ -1610,7 +1636,8 @@ describe('Workspaces Invites GQL', () => {
|
||||
await validateResourceAccess({
|
||||
shouldHaveAccess: true,
|
||||
expectedWorkspaceRole: Roles.Workspace.Guest,
|
||||
expectedProjectRole: Roles.Stream.Reviewer
|
||||
expectedProjectRole: Roles.Stream.Reviewer,
|
||||
streamId: myInviteTargetWorkspaceStream1.id
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1666,7 +1693,14 @@ describe('Workspaces Invites GQL', () => {
|
||||
expectedWorkspaceRole: withRole
|
||||
? Roles.Workspace.Admin
|
||||
: Roles.Workspace.Guest,
|
||||
expectedProjectRole: withRole ? Roles.Stream.Owner : Roles.Stream.Reviewer
|
||||
expectedProjectRole: withRole ? Roles.Stream.Owner : Roles.Stream.Reviewer,
|
||||
streamId: myInviteTargetWorkspaceStream1.id
|
||||
})
|
||||
|
||||
// ws admin will have access to everything
|
||||
await validateResourceAccess({
|
||||
shouldHaveAccess: { workspace: true, project: withRole ? true : false },
|
||||
streamId: myInviteTargetPrivateWorkspaceStream1.id
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { db } from '@/db/knex'
|
||||
import { StreamAcl } from '@/modules/core/dbSchema'
|
||||
import { ProjectRecordVisibility } from '@/modules/core/helpers/types'
|
||||
import { grantStreamPermissionsFactory } from '@/modules/core/repositories/streams'
|
||||
import { WorkspaceSeatType } from '@/modules/gatekeeper/domain/billing'
|
||||
import { getWorkspaceUserSeatsFactory } from '@/modules/gatekeeper/repositories/workspaceSeat'
|
||||
@@ -10,16 +12,25 @@ import {
|
||||
createTestWorkspace
|
||||
} from '@/modules/workspaces/tests/helpers/creation'
|
||||
import { describeEach, itEach } from '@/test/assertionHelper'
|
||||
import { BasicTestUser, createTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import {
|
||||
BasicTestUser,
|
||||
createTestUser,
|
||||
createTestUsers,
|
||||
login
|
||||
} from '@/test/authHelper'
|
||||
import {
|
||||
ActiveUserProjectsDocument,
|
||||
ActiveUserProjectsWorkspaceDocument,
|
||||
CreateProjectDocument,
|
||||
CreateWorkspaceProjectDocument,
|
||||
GetProjectDocument,
|
||||
GetWorkspaceDocument,
|
||||
GetWorkspaceProjectsDocument,
|
||||
GetWorkspaceProjectsQuery,
|
||||
GetWorkspaceTeamDocument,
|
||||
MoveProjectToWorkspaceDocument,
|
||||
ProjectUpdateRoleInput,
|
||||
ProjectVisibility,
|
||||
UpdateProjectRoleDocument,
|
||||
UpdateWorkspaceProjectRoleDocument
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
@@ -41,7 +52,8 @@ import {
|
||||
Nullable,
|
||||
Optional,
|
||||
PaidWorkspacePlans,
|
||||
Roles
|
||||
Roles,
|
||||
WorkspacePlans
|
||||
} from '@speckle/shared'
|
||||
import { expect } from 'chai'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
@@ -97,6 +109,48 @@ describe('Workspace project GQL CRUD', () => {
|
||||
)
|
||||
})
|
||||
|
||||
describe('when creating project', () => {
|
||||
it('should have workspace visibility by default', async () => {
|
||||
const res = await apollo.execute(
|
||||
CreateWorkspaceProjectDocument,
|
||||
{
|
||||
input: {
|
||||
name: 'Test Default Project',
|
||||
workspaceId: workspace.id
|
||||
}
|
||||
},
|
||||
{ assertNoErrors: true }
|
||||
)
|
||||
|
||||
const project = res.data?.workspaceMutations?.projects.create
|
||||
expect(project).to.be.ok
|
||||
expect(project?.visibility).to.equal(ProjectVisibility.Workspace)
|
||||
})
|
||||
|
||||
it('should create the project in that workspace', async () => {
|
||||
const projectName = cryptoRandomString({ length: 6 })
|
||||
|
||||
const createRes = await apollo.execute(CreateWorkspaceProjectDocument, {
|
||||
input: {
|
||||
name: projectName,
|
||||
workspaceId: workspace.id
|
||||
}
|
||||
})
|
||||
|
||||
const getRes = await apollo.execute(GetWorkspaceProjectsDocument, {
|
||||
id: workspace.id
|
||||
})
|
||||
|
||||
const workspaceProject = getRes.data?.workspace.projects.items.find(
|
||||
(project) => project.name === projectName
|
||||
)
|
||||
|
||||
expect(createRes).to.not.haveGraphQLErrors()
|
||||
expect(getRes).to.not.haveGraphQLErrors()
|
||||
expect(workspaceProject).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
describe('when changing workspace project roles', () => {
|
||||
const workspaceGuest: BasicTestUser = {
|
||||
id: '',
|
||||
@@ -240,31 +294,6 @@ describe('Workspace project GQL CRUD', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('when specifying a workspace id during project creation', () => {
|
||||
it('should create the project in that workspace', async () => {
|
||||
const projectName = cryptoRandomString({ length: 6 })
|
||||
|
||||
const createRes = await apollo.execute(CreateWorkspaceProjectDocument, {
|
||||
input: {
|
||||
name: projectName,
|
||||
workspaceId: workspace.id
|
||||
}
|
||||
})
|
||||
|
||||
const getRes = await apollo.execute(GetWorkspaceProjectsDocument, {
|
||||
id: workspace.id
|
||||
})
|
||||
|
||||
const workspaceProject = getRes.data?.workspace.projects.items.find(
|
||||
(project) => project.name === projectName
|
||||
)
|
||||
|
||||
expect(createRes).to.not.haveGraphQLErrors()
|
||||
expect(getRes).to.not.haveGraphQLErrors()
|
||||
expect(workspaceProject).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
describe('when querying projects', () => {
|
||||
const PAGE_SIZE = 5
|
||||
const PAGE_COUNT = 3
|
||||
@@ -285,18 +314,36 @@ describe('Workspace project GQL CRUD', () => {
|
||||
email: '',
|
||||
name: 'Query Workspace Guest'
|
||||
}
|
||||
|
||||
const workspaceAdmin = serverMemberUser
|
||||
const workspaceAdmin2: BasicTestUser = {
|
||||
id: '',
|
||||
email: '',
|
||||
name: 'Query Workspace Admin 2'
|
||||
}
|
||||
|
||||
const workspaceMember: BasicTestUser = {
|
||||
id: '',
|
||||
email: '',
|
||||
name: 'Query Workspace Member'
|
||||
}
|
||||
const workspaceMemberNoExplicitRoles: BasicTestUser = {
|
||||
id: '',
|
||||
email: '',
|
||||
name: 'Query Workspace Member w/ No Explicit Project Roles'
|
||||
}
|
||||
|
||||
let wsProjects: BasicTestStream[]
|
||||
let nonWorkspaceProjects: BasicTestStream[]
|
||||
let apollo: TestApolloServer
|
||||
|
||||
before(async () => {
|
||||
await createTestUsers([workspaceGuest, workspaceMember])
|
||||
await createTestUsers([
|
||||
workspaceGuest,
|
||||
workspaceMember,
|
||||
workspaceAdmin2,
|
||||
workspaceMemberNoExplicitRoles
|
||||
])
|
||||
await createTestWorkspace(queryWorkspace, workspaceAdmin, {
|
||||
addPlan: { name: 'team', status: 'valid' }
|
||||
})
|
||||
@@ -312,6 +359,18 @@ describe('Workspace project GQL CRUD', () => {
|
||||
workspaceMember,
|
||||
Roles.Workspace.Member,
|
||||
WorkspaceSeatType.Editor
|
||||
],
|
||||
[
|
||||
queryWorkspace,
|
||||
workspaceAdmin2,
|
||||
Roles.Workspace.Admin,
|
||||
WorkspaceSeatType.Editor
|
||||
],
|
||||
[
|
||||
queryWorkspace,
|
||||
workspaceMemberNoExplicitRoles,
|
||||
Roles.Workspace.Member,
|
||||
WorkspaceSeatType.Editor
|
||||
]
|
||||
])
|
||||
wsProjects = times(
|
||||
@@ -320,7 +379,11 @@ describe('Workspace project GQL CRUD', () => {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: `Query Workspace Project - #${i}`,
|
||||
isPublic: false, // have to be private for tests below
|
||||
// Make all except the very last one workspace visibility
|
||||
visibility:
|
||||
i === TOTAL_WS_PROJECT_COUNT - 1
|
||||
? ProjectRecordVisibility.Private
|
||||
: ProjectRecordVisibility.Workspace,
|
||||
workspaceId: queryWorkspace.id
|
||||
})
|
||||
)
|
||||
@@ -330,7 +393,7 @@ describe('Workspace project GQL CRUD', () => {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: `Non Workspace Project - #${i}`,
|
||||
isPublic: false
|
||||
visibility: ProjectRecordVisibility.Private
|
||||
})
|
||||
)
|
||||
|
||||
@@ -351,8 +414,9 @@ describe('Workspace project GQL CRUD', () => {
|
||||
)
|
||||
|
||||
await Promise.all([
|
||||
// Add explicit single assignment to workspaceMember to 1st non-workspace project
|
||||
// Add explicit single assignment to workspaceMember & workspaceAdmin to 1st non-workspace project
|
||||
addToStream(nonWorkspaceProjects[0], workspaceMember, Roles.Stream.Contributor),
|
||||
addToStream(nonWorkspaceProjects[0], workspaceAdmin, Roles.Stream.Contributor),
|
||||
// Add explicit single assignment to workspaceMember to 1st workspace project
|
||||
addToStream(wsProjects[0], workspaceMember, Roles.Stream.Contributor)
|
||||
])
|
||||
@@ -362,12 +426,18 @@ describe('Workspace project GQL CRUD', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// projects at the end have no explicit project assignments (and very last one is fully private),
|
||||
// and first X ones are explicitly assigned to guest user
|
||||
const implicitPrivateProject = () => wsProjects.at(-1)!
|
||||
const implicitWorkspaceVisibilityProject = () => wsProjects.at(-2)!
|
||||
const explicitGuestProject = () => wsProjects.at(0)!
|
||||
|
||||
afterEach(async () => {
|
||||
adminOverrideMock.disable()
|
||||
})
|
||||
|
||||
describe('through Workspace.projects', () => {
|
||||
it('should return all projects for workspace members', async () => {
|
||||
it('should return all projects for workspace admin', async () => {
|
||||
const res = await apollo.execute(GetWorkspaceProjectsDocument, {
|
||||
id: queryWorkspace.id,
|
||||
limit: 999 // get everything
|
||||
@@ -443,6 +513,24 @@ describe('Workspace project GQL CRUD', () => {
|
||||
expect(collection?.totalCount).to.equal(GUEST_PROJECT_COUNT)
|
||||
})
|
||||
|
||||
it('should return all non-private for members who may not even have any explicit project roles', async () => {
|
||||
const apollo = await testApolloServer({
|
||||
authUserId: workspaceMemberNoExplicitRoles.id
|
||||
})
|
||||
const res = await apollo.execute(GetWorkspaceProjectsDocument, {
|
||||
id: queryWorkspace.id,
|
||||
limit: 999 // get everything
|
||||
})
|
||||
|
||||
const nonPrivateCount = TOTAL_WS_PROJECT_COUNT - 1 // -1 for the fully private one
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
const collection = res.data?.workspace.projects
|
||||
expect(collection?.items.length).to.equal(nonPrivateCount)
|
||||
expect(collection?.cursor).to.be.ok
|
||||
expect(collection?.totalCount).to.equal(nonPrivateCount)
|
||||
})
|
||||
|
||||
it('should respect limits', async () => {
|
||||
const res = await apollo.execute(GetWorkspaceProjectsDocument, {
|
||||
id: queryWorkspace.id,
|
||||
@@ -542,17 +630,36 @@ describe('Workspace project GQL CRUD', () => {
|
||||
await createTestUser(randomServerGuy)
|
||||
})
|
||||
|
||||
// projects at the end have no explicit project assignments,
|
||||
// and first X ones are explicitly assigned to guest user
|
||||
const implicitProject = () => wsProjects.at(-1)!
|
||||
const explicitGuestProject = () => wsProjects.at(0)!
|
||||
|
||||
it('it should be accessible to workspace member', async () => {
|
||||
it('workspace visibility should be accessible to workspace member', async () => {
|
||||
const apollo = await testApolloServer({
|
||||
authUserId: workspaceMember.id
|
||||
})
|
||||
const res = await apollo.execute(GetProjectDocument, {
|
||||
id: implicitProject().id
|
||||
id: implicitWorkspaceVisibilityProject().id
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.project.id).to.be.ok
|
||||
})
|
||||
|
||||
it('private visibility should not be accessible to workspace member w/o explicit role', async () => {
|
||||
const apollo = await testApolloServer({
|
||||
authUserId: workspaceMember.id
|
||||
})
|
||||
const res = await apollo.execute(GetProjectDocument, {
|
||||
id: implicitPrivateProject().id
|
||||
})
|
||||
|
||||
expect(res).to.haveGraphQLErrors()
|
||||
expect(res.data?.project).to.not.be.ok
|
||||
})
|
||||
|
||||
it('private visibility should be accessible to workspace admin w/o explicit role', async () => {
|
||||
const apollo = await testApolloServer({
|
||||
authUserId: workspaceAdmin2.id
|
||||
})
|
||||
const res = await apollo.execute(GetProjectDocument, {
|
||||
id: implicitPrivateProject().id
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
@@ -564,7 +671,7 @@ describe('Workspace project GQL CRUD', () => {
|
||||
authUserId: randomServerGuy.id
|
||||
})
|
||||
const res = await apollo.execute(GetProjectDocument, {
|
||||
id: implicitProject().id
|
||||
id: implicitWorkspaceVisibilityProject().id
|
||||
})
|
||||
|
||||
expect(res).to.haveGraphQLErrors()
|
||||
@@ -582,7 +689,9 @@ describe('Workspace project GQL CRUD', () => {
|
||||
authUserId: workspaceGuest.id
|
||||
})
|
||||
const res = await apollo.execute(GetProjectDocument, {
|
||||
id: explicit ? explicitGuestProject().id : implicitProject().id
|
||||
id: explicit
|
||||
? explicitGuestProject().id
|
||||
: implicitWorkspaceVisibilityProject().id
|
||||
})
|
||||
|
||||
if (explicit) {
|
||||
@@ -599,8 +708,8 @@ describe('Workspace project GQL CRUD', () => {
|
||||
[{ adminOverrideEnabled: true }, { adminOverrideEnabled: false }],
|
||||
({ adminOverrideEnabled }) =>
|
||||
adminOverrideEnabled
|
||||
? 'it should return project for server admins if override enabled'
|
||||
: 'it should not return project for server admins if override disabled',
|
||||
? 'it should return fully private project for server admins if override enabled'
|
||||
: 'it should not return fully private project for server admins if override disabled',
|
||||
async ({ adminOverrideEnabled }) => {
|
||||
const apollo = await testApolloServer({
|
||||
authUserId: serverAdminUser.id
|
||||
@@ -608,7 +717,7 @@ describe('Workspace project GQL CRUD', () => {
|
||||
|
||||
adminOverrideMock.enable(adminOverrideEnabled)
|
||||
const res = await apollo.execute(GetProjectDocument, {
|
||||
id: implicitProject().id
|
||||
id: implicitPrivateProject().id
|
||||
})
|
||||
|
||||
if (adminOverrideEnabled) {
|
||||
@@ -671,28 +780,41 @@ describe('Workspace project GQL CRUD', () => {
|
||||
]).to.deep.equalInAnyOrder([nonWorkspaceProjects[0].id, wsProjects[0].id])
|
||||
})
|
||||
|
||||
it('should return all projects user is explicitly or implicitly assigned to, if flag set', async () => {
|
||||
const apolloMember = await testApolloServer({
|
||||
authUserId: workspaceMember.id
|
||||
})
|
||||
const memberRes = await apolloMember.execute(
|
||||
ActiveUserProjectsWorkspaceDocument,
|
||||
{ limit: 999, filter: { includeImplicitAccess: true } },
|
||||
{ assertNoErrors: true }
|
||||
)
|
||||
const memberCollection = memberRes.data?.activeUser?.projects
|
||||
itEach(
|
||||
[{ admin: true }, { admin: false }],
|
||||
({ admin }) =>
|
||||
`should return all projects ${
|
||||
admin ? 'ws admin' : 'ws member'
|
||||
} is explicitly or implicitly assigned to, if flag set`,
|
||||
async ({ admin }) => {
|
||||
const apollo = await testApolloServer({
|
||||
authUserId: admin ? workspaceAdmin.id : workspaceMember.id
|
||||
})
|
||||
const res = await apollo.execute(
|
||||
ActiveUserProjectsWorkspaceDocument,
|
||||
{ limit: 999, filter: { includeImplicitAccess: true } },
|
||||
{ assertNoErrors: true }
|
||||
)
|
||||
const projects = res.data?.activeUser?.projects
|
||||
|
||||
// 1 non-workspace assignment + all workspace projects
|
||||
const expectedMemberCount = TOTAL_WS_PROJECT_COUNT + 1
|
||||
// 1 non-workspace assignment + all workspace projects
|
||||
// (except the last one thats fully private, if not admin)
|
||||
let expectedCount = TOTAL_WS_PROJECT_COUNT + 1
|
||||
if (!admin) {
|
||||
expectedCount -= 1
|
||||
}
|
||||
|
||||
expect(memberCollection).to.be.ok
|
||||
expect(memberCollection!.totalCount).to.equal(expectedMemberCount)
|
||||
expect(memberCollection!.items.length).to.equal(expectedMemberCount)
|
||||
expect(memberCollection!.items.map((i) => i.id)).to.deep.equalInAnyOrder([
|
||||
nonWorkspaceProjects[0].id,
|
||||
...wsProjects.map((p) => p.id)
|
||||
])
|
||||
})
|
||||
expect(projects).to.be.ok
|
||||
expect(projects!.totalCount).to.equal(expectedCount)
|
||||
expect(projects!.items.length).to.equal(expectedCount)
|
||||
expect(projects!.items.map((i) => i.id)).to.deep.equalInAnyOrder([
|
||||
nonWorkspaceProjects[0].id,
|
||||
...wsProjects
|
||||
.filter((p) => (admin ? true : p.id !== implicitPrivateProject().id))
|
||||
.map((p) => p.id)
|
||||
])
|
||||
}
|
||||
)
|
||||
|
||||
it('should only return workspace projects if filter set', async () => {
|
||||
const res = await apollo.execute(ActiveUserProjectsWorkspaceDocument, {
|
||||
@@ -739,7 +861,7 @@ describe('Workspace project GQL CRUD', () => {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: 'Test Project',
|
||||
isPublic: false
|
||||
visibility: ProjectRecordVisibility.Private
|
||||
}
|
||||
|
||||
const targetWorkspace: BasicTestWorkspace = {
|
||||
@@ -750,7 +872,9 @@ describe('Workspace project GQL CRUD', () => {
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await createTestWorkspace(targetWorkspace, serverAdminUser)
|
||||
await createTestWorkspace(targetWorkspace, serverAdminUser, {
|
||||
addPlan: WorkspacePlans.Unlimited
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -762,17 +886,38 @@ describe('Workspace project GQL CRUD', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should move the project to the target workspace', async () => {
|
||||
it('should move the project to the target workspace and update visibility', async () => {
|
||||
const res = await apollo.execute(MoveProjectToWorkspaceDocument, {
|
||||
projectId: testProject.id,
|
||||
workspaceId: targetWorkspace.id
|
||||
})
|
||||
|
||||
const { workspaceId } =
|
||||
res.data?.workspaceMutations.projects.moveToWorkspace ?? {}
|
||||
const project = res.data?.workspaceMutations.projects.moveToWorkspace
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(workspaceId).to.equal(targetWorkspace.id)
|
||||
expect(project?.workspaceId).to.equal(targetWorkspace.id)
|
||||
expect(project?.visibility).to.equal(ProjectVisibility.Workspace)
|
||||
})
|
||||
|
||||
it('should move a public project to the target workspace and keep same visibility', async () => {
|
||||
const publicProject: BasicTestStream = {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: 'Test Public Project',
|
||||
visibility: ProjectRecordVisibility.Public
|
||||
}
|
||||
await createTestStream(publicProject, serverAdminUser)
|
||||
|
||||
const res = await apollo.execute(MoveProjectToWorkspaceDocument, {
|
||||
projectId: publicProject.id,
|
||||
workspaceId: targetWorkspace.id
|
||||
})
|
||||
|
||||
const project = res.data?.workspaceMutations.projects.moveToWorkspace
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(project?.workspaceId).to.equal(targetWorkspace.id)
|
||||
expect(project?.visibility).to.equal(ProjectVisibility.Public)
|
||||
})
|
||||
|
||||
it('should preserve project roles for project members with editor seats', async () => {
|
||||
@@ -839,4 +984,224 @@ describe('Workspace project GQL CRUD', () => {
|
||||
expect(adminWorkspaceRole?.role).to.equal(Roles.Workspace.Admin)
|
||||
})
|
||||
})
|
||||
|
||||
// moved over Alessandro's tests from core to here, since they are all related to workspaces
|
||||
// they're kind of a mess and need to be cleaned up
|
||||
describe('query user.projects', () => {
|
||||
it('should return projects not in a workspace', async () => {
|
||||
const testAdminUser: BasicTestUser = {
|
||||
id: '',
|
||||
name: 'test',
|
||||
email: '',
|
||||
role: Roles.Server.Admin,
|
||||
verified: true
|
||||
}
|
||||
await createTestUser(testAdminUser)
|
||||
const workspace = {
|
||||
id: '',
|
||||
name: 'test ws',
|
||||
slug: cryptoRandomString({ length: 10 }),
|
||||
ownerId: ''
|
||||
}
|
||||
await createTestWorkspace(workspace, testAdminUser)
|
||||
|
||||
const session = await login(testAdminUser)
|
||||
const getWorkspaceRes = await session.execute(GetWorkspaceDocument, {
|
||||
workspaceId: workspace.id
|
||||
})
|
||||
|
||||
expect(getWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
const workspaceId = getWorkspaceRes.data!.workspace.id
|
||||
|
||||
const createProjectInWorkspaceRes = await session.execute(
|
||||
CreateWorkspaceProjectDocument,
|
||||
{ input: { name: 'project', workspaceId } }
|
||||
)
|
||||
expect(createProjectInWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const createProjectNonInWorkspaceRes = await session.execute(
|
||||
CreateProjectDocument,
|
||||
{ input: { name: 'project' } }
|
||||
)
|
||||
expect(createProjectNonInWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
const projectNonInWorkspace =
|
||||
createProjectNonInWorkspaceRes.data!.projectMutations.create
|
||||
|
||||
const userProjectsRes = await session.execute(ActiveUserProjectsDocument, {
|
||||
filter: { personalOnly: true }
|
||||
})
|
||||
expect(userProjectsRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const projects = userProjectsRes.data!.activeUser!.projects.items
|
||||
|
||||
expect(projects).to.have.length(1)
|
||||
expect(projects[0].id).to.eq(projectNonInWorkspace.id)
|
||||
})
|
||||
|
||||
it('should return projects in workspace', async () => {
|
||||
const testAdminUser: BasicTestUser = {
|
||||
id: '',
|
||||
name: 'test',
|
||||
email: '',
|
||||
role: Roles.Server.Admin,
|
||||
verified: true
|
||||
}
|
||||
await createTestUser(testAdminUser)
|
||||
const workspace = {
|
||||
id: '',
|
||||
name: 'test ws',
|
||||
slug: cryptoRandomString({ length: 10 }),
|
||||
ownerId: ''
|
||||
}
|
||||
await createTestWorkspace(workspace, testAdminUser)
|
||||
|
||||
const session = await login(testAdminUser)
|
||||
const getWorkspaceRes = await session.execute(GetWorkspaceDocument, {
|
||||
workspaceId: workspace.id
|
||||
})
|
||||
|
||||
expect(getWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
const workspaceId = getWorkspaceRes.data!.workspace.id
|
||||
|
||||
const createProjectInWorkspaceRes = await session.execute(
|
||||
CreateWorkspaceProjectDocument,
|
||||
{ input: { name: 'project', workspaceId } }
|
||||
)
|
||||
expect(createProjectInWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
const projectInWorkspace =
|
||||
createProjectInWorkspaceRes.data!.workspaceMutations.projects.create
|
||||
|
||||
const createProjectNonInWorkspaceRes = await session.execute(
|
||||
CreateProjectDocument,
|
||||
{ input: { name: 'project' } }
|
||||
)
|
||||
expect(createProjectNonInWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const userProjectsRes = await session.execute(ActiveUserProjectsDocument, {
|
||||
filter: { workspaceId }
|
||||
})
|
||||
expect(userProjectsRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const projects = userProjectsRes.data!.activeUser!.projects.items
|
||||
|
||||
expect(projects).to.have.length(1)
|
||||
expect(projects[0].id).to.eq(projectInWorkspace.id)
|
||||
})
|
||||
|
||||
it('should return all user projects', async () => {
|
||||
const testAdminUser: BasicTestUser = {
|
||||
id: '',
|
||||
name: 'test',
|
||||
email: '',
|
||||
role: Roles.Server.Admin,
|
||||
verified: true
|
||||
}
|
||||
await createTestUser(testAdminUser)
|
||||
const workspace = {
|
||||
id: '',
|
||||
name: 'test ws',
|
||||
slug: cryptoRandomString({ length: 10 }),
|
||||
ownerId: ''
|
||||
}
|
||||
await createTestWorkspace(workspace, testAdminUser)
|
||||
|
||||
const session = await login(testAdminUser)
|
||||
const getWorkspaceRes = await session.execute(GetWorkspaceDocument, {
|
||||
workspaceId: workspace.id
|
||||
})
|
||||
|
||||
expect(getWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
const workspaceId = getWorkspaceRes.data!.workspace.id
|
||||
|
||||
const createProjectInWorkspaceRes = await session.execute(
|
||||
CreateWorkspaceProjectDocument,
|
||||
{ input: { name: 'project', workspaceId } }
|
||||
)
|
||||
expect(createProjectInWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const createProjectNonInWorkspaceRes = await session.execute(
|
||||
CreateProjectDocument,
|
||||
{ input: { name: 'project' } }
|
||||
)
|
||||
expect(createProjectNonInWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const userProjectsRes = await session.execute(ActiveUserProjectsDocument, {
|
||||
filter: {}
|
||||
})
|
||||
expect(userProjectsRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const projects = userProjectsRes.data!.activeUser!.projects.items
|
||||
|
||||
expect(projects).to.have.length(2)
|
||||
})
|
||||
|
||||
it('should return all user projects sorted by user role', async () => {
|
||||
const testAdminUser: BasicTestUser = {
|
||||
id: '',
|
||||
name: 'test',
|
||||
email: '',
|
||||
role: Roles.Server.Admin,
|
||||
verified: true
|
||||
}
|
||||
await createTestUser(testAdminUser)
|
||||
const workspace = {
|
||||
id: '',
|
||||
name: 'test ws',
|
||||
slug: cryptoRandomString({ length: 10 }),
|
||||
ownerId: ''
|
||||
}
|
||||
await createTestWorkspace(workspace, testAdminUser)
|
||||
|
||||
const session = await login(testAdminUser)
|
||||
const getWorkspaceRes = await session.execute(GetWorkspaceDocument, {
|
||||
workspaceId: workspace.id
|
||||
})
|
||||
|
||||
expect(getWorkspaceRes).to.not.haveGraphQLErrors()
|
||||
const workspaceId = getWorkspaceRes.data!.workspace.id
|
||||
|
||||
const createProjectInWorkspaceAsOwnerRes = await session.execute(
|
||||
CreateWorkspaceProjectDocument,
|
||||
{ input: { name: 'project', workspaceId } }
|
||||
)
|
||||
expect(createProjectInWorkspaceAsOwnerRes).to.not.haveGraphQLErrors()
|
||||
const createProjectInWorkspaceAsContributorRes = await session.execute(
|
||||
CreateWorkspaceProjectDocument,
|
||||
{ input: { name: 'project 2', workspaceId } }
|
||||
)
|
||||
expect(createProjectInWorkspaceAsContributorRes).to.not.haveGraphQLErrors()
|
||||
const projectContributorId =
|
||||
createProjectInWorkspaceAsContributorRes.data?.workspaceMutations.projects
|
||||
.create.id
|
||||
await db(StreamAcl.name)
|
||||
.update({ role: Roles.Stream.Contributor })
|
||||
.where({ userId: testAdminUser.id, resourceId: projectContributorId })
|
||||
const createProjectInWorkspaceAsReviewerRes = await session.execute(
|
||||
CreateWorkspaceProjectDocument,
|
||||
{ input: { name: 'project 3', workspaceId } }
|
||||
)
|
||||
expect(createProjectInWorkspaceAsReviewerRes).to.not.haveGraphQLErrors()
|
||||
const projectReviewerId =
|
||||
createProjectInWorkspaceAsReviewerRes.data?.workspaceMutations.projects.create
|
||||
.id
|
||||
await db(StreamAcl.name)
|
||||
.update({ role: Roles.Stream.Reviewer })
|
||||
.where({ userId: testAdminUser.id, resourceId: projectReviewerId })
|
||||
|
||||
const userProjectsRes = await session.execute(ActiveUserProjectsDocument, {
|
||||
filter: {},
|
||||
sortBy: ['role']
|
||||
})
|
||||
expect(userProjectsRes).to.not.haveGraphQLErrors()
|
||||
|
||||
const projects = userProjectsRes.data!.activeUser!.projects.items
|
||||
|
||||
expect(projects).to.have.length(3)
|
||||
expect(projects[0].id).to.eq(
|
||||
createProjectInWorkspaceAsOwnerRes.data?.workspaceMutations.projects.create.id
|
||||
)
|
||||
expect(projects[1].id).to.eq(projectContributorId)
|
||||
expect(projects[2].id).to.eq(projectReviewerId)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Streams } from '@/modules/core/dbSchema'
|
||||
import { AllScopes } from '@/modules/core/helpers/mainConstants'
|
||||
import { ProjectRecordVisibility } from '@/modules/core/helpers/types'
|
||||
import {
|
||||
assignToWorkspace,
|
||||
BasicTestWorkspace,
|
||||
@@ -283,7 +284,6 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: Viewer vs Editor
|
||||
describe('in a workspace with projects', () => {
|
||||
const workspace: BasicTestWorkspace = {
|
||||
id: '',
|
||||
@@ -326,35 +326,43 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: 'Project A',
|
||||
isPublic: false
|
||||
visibility: ProjectRecordVisibility.Workspace
|
||||
}
|
||||
|
||||
const workspaceProjectB: BasicTestStream = {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: 'Project B',
|
||||
isPublic: false
|
||||
visibility: ProjectRecordVisibility.Workspace
|
||||
}
|
||||
|
||||
const workspaceProjectC: BasicTestStream = {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: 'Project C',
|
||||
isPublic: false
|
||||
visibility: ProjectRecordVisibility.Workspace
|
||||
}
|
||||
|
||||
const workspaceProjectD: BasicTestStream = {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: 'Project D',
|
||||
isPublic: false
|
||||
visibility: ProjectRecordVisibility.Workspace
|
||||
}
|
||||
|
||||
const workspaceProjectE: BasicTestStream = {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
name: 'Project E (Fully private)',
|
||||
visibility: ProjectRecordVisibility.Private
|
||||
}
|
||||
|
||||
const workspaceProjects = [
|
||||
workspaceProjectA,
|
||||
workspaceProjectB,
|
||||
workspaceProjectC,
|
||||
workspaceProjectD
|
||||
workspaceProjectD,
|
||||
workspaceProjectE
|
||||
]
|
||||
|
||||
before(async () => {
|
||||
@@ -424,13 +432,13 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
*
|
||||
* Initial explicit workspace project roles:
|
||||
*
|
||||
* | | Project A | Project B | Project C | Project D |
|
||||
* |---------------------------|-------------|-------------|-----------|-----------|
|
||||
* | workspaceAdminUser | Owner | None | None | None |
|
||||
* | workspaceMemberUser | Owner | Contributor | Reviewer | None |
|
||||
* | workspaceGuestUser | Contributor | Reviewer | None | None |
|
||||
* | workspaceMemberViewerUser | Reviewer | None | None | None |
|
||||
* | workspaceGuestViewerUser | None | Reviewer | None | None |
|
||||
* | | Project A | Project B | Project C | Project D | Project E (private) |
|
||||
* |---------------------------|-------------|-------------|-----------|-----------|---------------------|
|
||||
* | workspaceAdminUser | Owner | None | None | None | None
|
||||
* | workspaceMemberUser | Owner | Contributor | Reviewer | None | None
|
||||
* | workspaceGuestUser | Contributor | Reviewer | None | None | Reviewer
|
||||
* | workspaceMemberViewerUser | Reviewer | None | None | None | Reviewer
|
||||
* | workspaceGuestViewerUser | None | Reviewer | None | None | Reviewer
|
||||
*/
|
||||
|
||||
await Promise.all([
|
||||
@@ -448,7 +456,15 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
addToStream(workspaceProjectB, workspaceGuestUser, Roles.Stream.Reviewer),
|
||||
addToStream(workspaceProjectB, workspaceGuestViewerUser, Roles.Stream.Reviewer),
|
||||
// C
|
||||
addToStream(workspaceProjectC, workspaceMemberUser, Roles.Stream.Reviewer)
|
||||
addToStream(workspaceProjectC, workspaceMemberUser, Roles.Stream.Reviewer),
|
||||
// E
|
||||
addToStream(workspaceProjectE, workspaceGuestUser, Roles.Stream.Reviewer),
|
||||
addToStream(
|
||||
workspaceProjectE,
|
||||
workspaceMemberViewerUser,
|
||||
Roles.Stream.Reviewer
|
||||
),
|
||||
addToStream(workspaceProjectE, workspaceGuestViewerUser, Roles.Stream.Reviewer)
|
||||
])
|
||||
})
|
||||
|
||||
@@ -470,15 +486,16 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
user: workspaceAdminUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isOwner)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).hasExplicitRole).to.be.not.ok
|
||||
})
|
||||
|
||||
it('workspaceMemberUser is implicit reviewer in all of them, and also has explicit roles in some', async () => {
|
||||
it('workspaceMemberUser is implicit reviewer in all of them, except E, and also has explicit roles in some', async () => {
|
||||
const { projects, checkAllProjects, checkProject } = await getProjects({
|
||||
user: workspaceMemberUser
|
||||
})
|
||||
@@ -489,42 +506,46 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
expect(checkProject(workspaceProjectB).isExplicitContributor).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).hasAccess).to.be.not.ok
|
||||
})
|
||||
|
||||
it('workspaceGuestUser only has explicit roles in 2 projects', async () => {
|
||||
it('workspaceGuestUser only has explicit roles in 3 projects', async () => {
|
||||
const { projects, checkProject } = await getProjects({
|
||||
user: workspaceGuestUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(2)
|
||||
expect(projects.length).to.eq(3)
|
||||
expect(checkProject(workspaceProjectA).isExplicitContributor).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectC).hasAccess).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasAccess).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitReviewer).to.be.ok
|
||||
})
|
||||
|
||||
it('workspaceMemberViewerUser is only explicit reviewer in 1 project, and has implicit roles elsewhere', async () => {
|
||||
it('workspaceMemberViewerUser is only explicit reviewer in 2 projects, and has implicit roles elsewhere', async () => {
|
||||
const { projects, checkAllProjects, checkProject } = await getProjects({
|
||||
user: workspaceMemberViewerUser
|
||||
})
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isReviewer)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitReviewer).to.be.ok
|
||||
})
|
||||
|
||||
it('workspaceGuestViewerUser is only explicit reviewer in 1 project', async () => {
|
||||
it('workspaceGuestViewerUser is only explicit reviewer in 2 projects', async () => {
|
||||
const { projects, checkProject } = await getProjects({
|
||||
user: workspaceGuestViewerUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(1)
|
||||
expect(projects.length).to.eq(2)
|
||||
expect(checkProject(workspaceProjectB).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitReviewer).to.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -582,9 +603,10 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
user: workspaceGuestUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(2)
|
||||
expect(projects.length).to.eq(3)
|
||||
expect(checkProject(workspaceProjectA).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitReviewer).to.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -605,17 +627,18 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should still remain explicit owner and be implicit reviewer elsewhere', async () => {
|
||||
it('should still remain explicit owner and be implicit reviewer elsewhere, except private E', async () => {
|
||||
const { projects, checkAllProjects, checkProject } = await getProjects({
|
||||
user: workspaceAdminUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(checkAllProjects((p) => p.isReviewer)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkAllProjects((p) => p.isReviewer)).to.be.ok
|
||||
expect(checkProject(workspaceProjectE).hasAccess).to.be.not.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -670,12 +693,13 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
user: workspaceMemberUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isOwner)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.not.be.ok
|
||||
expect(checkProject(workspaceProjectE).hasExplicitRole).to.not.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -704,6 +728,7 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
expect(checkProject(workspaceProjectB).isExplicitContributor).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).hasExplicitRole).to.be.not.ok
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -729,12 +754,13 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
user: workspaceGuestUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isOwner)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.not.be.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitOwner).to.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -758,12 +784,13 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
user: workspaceGuestUser
|
||||
})
|
||||
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isReviewer)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).isExplicitContributor).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitReviewer).to.be.ok
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -791,12 +818,13 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
})
|
||||
|
||||
expect(workspace.seatType).to.eq(WorkspaceSeatType.Editor)
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isOwner)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectB).hasExplicitRole).to.not.be.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.not.be.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.not.be.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitOwner).to.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -821,8 +849,9 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
})
|
||||
|
||||
expect(workspace.seatType).to.eq(WorkspaceSeatType.Viewer)
|
||||
expect(projects.length).to.eq(1)
|
||||
expect(projects.length).to.eq(2)
|
||||
expect(checkProject(workspaceProjectA).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitReviewer).to.be.ok
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -850,12 +879,13 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
})
|
||||
|
||||
expect(workspace.seatType).to.eq(WorkspaceSeatType.Editor)
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isOwner)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).hasExplicitRole).to.not.be.ok
|
||||
expect(checkProject(workspaceProjectB).isExplicitOwner).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.not.be.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitOwner).to.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
@@ -874,19 +904,20 @@ describe('Workspaces Roles/Seats GQL', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should retain viewer seat, same explicit access and get full implicit acccess', async () => {
|
||||
it('should retain viewer seat, same explicit access and get full workspace visibility implicit acccess', async () => {
|
||||
const { workspace, projects, checkProject, checkAllProjects } =
|
||||
await getProjects({
|
||||
user: workspaceGuestViewerUser
|
||||
})
|
||||
|
||||
expect(workspace.seatType).to.eq(WorkspaceSeatType.Viewer)
|
||||
expect(projects.length).to.eq(4)
|
||||
expect(projects.length).to.eq(5)
|
||||
expect(checkAllProjects((p) => p.isReviewer)).to.be.ok
|
||||
expect(checkProject(workspaceProjectA).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectB).isExplicitReviewer).to.be.ok
|
||||
expect(checkProject(workspaceProjectC).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectD).hasExplicitRole).to.be.not.ok
|
||||
expect(checkProject(workspaceProjectE).isExplicitReviewer).to.be.ok
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ProjectRecordVisibility } from '@/modules/core/helpers/types'
|
||||
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
|
||||
import {
|
||||
assignToWorkspaces,
|
||||
@@ -110,9 +111,9 @@ const { FF_WORKSPACES_SSO_ENABLED } = getFeatureFlags()
|
||||
const testProject: BasicTestStream = {
|
||||
id: '',
|
||||
ownerId: '',
|
||||
isPublic: false,
|
||||
name: 'Workspace Project',
|
||||
workspaceId: testWorkspaceWithSso.id
|
||||
workspaceId: testWorkspaceWithSso.id,
|
||||
visibility: ProjectRecordVisibility.Workspace
|
||||
}
|
||||
|
||||
await createTestStream(testProject, workspaceAdmin)
|
||||
|
||||
Reference in New Issue
Block a user