From 8a9659f22367686cb012662ce5a9bdd509b613c3 Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Thu, 1 Aug 2024 09:36:52 +0100 Subject: [PATCH] feat(workspaces): workspace member role resolvers (#2540) * feat(workspaces): update/delete workspace role resolvers * chore(workspaces): gql tests for role changes * fix(workspaces): test syntax oops * fix(workspaces): got it twisted * fix(workspaces): simplify api surface, better return types * fix(workspaces): correct role type usage, improve tests * fix(workspaces): authorize in resolver * fix(workspaces): correct usage of authorizeResolver --- .../typedefs/workspaces.graphql | 10 +- .../modules/core/graph/generated/graphql.ts | 11 +- .../graph/generated/graphql.ts | 10 +- .../workspaces/events/eventListener.ts | 6 +- .../workspaces/graph/mocks/workspaces.ts | 11 -- .../workspaces/graph/resolvers/workspaces.ts | 93 ++++++---- .../modules/workspaces/helpers/roles.ts | 4 + packages/server/modules/workspaces/index.ts | 4 +- .../modules/workspaces/services/invites.ts | 6 +- .../modules/workspaces/services/management.ts | 8 +- .../workspaces/tests/helpers/creation.ts | 6 +- .../tests/integration/roles.graph.spec.ts | 175 ++++++++++++++++++ .../tests/unit/services/management.spec.ts | 30 +-- .../server/test/graphql/generated/graphql.ts | 37 ++-- packages/server/test/graphql/workspaces.ts | 25 ++- 15 files changed, 324 insertions(+), 112 deletions(-) create mode 100644 packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 1a4bbcd5d..53973eba1 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -27,7 +27,10 @@ input WorkspaceUpdateInput { input WorkspaceRoleUpdateInput { userId: String! workspaceId: String! - role: WorkspaceRole! + """ + Leave role null to revoke access entirely + """ + role: String } input WorkspaceRoleDeleteInput { @@ -45,13 +48,8 @@ type WorkspaceMutations { @hasScope(scope: "workspace:create") delete(workspaceId: String!): Boolean! @hasScope(scope: "workspace:delete") update(input: WorkspaceUpdateInput!): Workspace! @hasScope(scope: "workspace:update") - """ - TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes - """ updateRole(input: WorkspaceRoleUpdateInput!): Workspace! @hasScope(scope: "workspace:update") - deleteRole(input: WorkspaceRoleDeleteInput!): Workspace! - @hasScope(scope: "workspace:update") invites: WorkspaceInviteMutations! } diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 817cdbacf..c43f00bb2 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -3828,10 +3828,8 @@ export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; create: Workspace; delete: Scalars['Boolean']['output']; - deleteRole: Workspace; invites: WorkspaceInviteMutations; update: Workspace; - /** TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes */ updateRole: Workspace; }; @@ -3846,11 +3844,6 @@ export type WorkspaceMutationsDeleteArgs = { }; -export type WorkspaceMutationsDeleteRoleArgs = { - input: WorkspaceRoleDeleteInput; -}; - - export type WorkspaceMutationsUpdateArgs = { input: WorkspaceUpdateInput; }; @@ -3877,7 +3870,8 @@ export type WorkspaceRoleDeleteInput = { }; export type WorkspaceRoleUpdateInput = { - role: WorkspaceRole; + /** Leave role null to revoke access entirely */ + role?: InputMaybe; userId: Scalars['String']['input']; workspaceId: Scalars['String']['input']; }; @@ -5742,7 +5736,6 @@ export type WorkspaceInviteMutationsResolvers = { create?: Resolver>; delete?: Resolver>; - deleteRole?: Resolver>; invites?: Resolver; update?: Resolver>; updateRole?: Resolver>; diff --git a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts index b607ae923..8dcc7d73e 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -3817,10 +3817,8 @@ export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; create: Workspace; delete: Scalars['Boolean']['output']; - deleteRole: Workspace; invites: WorkspaceInviteMutations; update: Workspace; - /** TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes */ updateRole: Workspace; }; @@ -3835,11 +3833,6 @@ export type WorkspaceMutationsDeleteArgs = { }; -export type WorkspaceMutationsDeleteRoleArgs = { - input: WorkspaceRoleDeleteInput; -}; - - export type WorkspaceMutationsUpdateArgs = { input: WorkspaceUpdateInput; }; @@ -3866,7 +3859,8 @@ export type WorkspaceRoleDeleteInput = { }; export type WorkspaceRoleUpdateInput = { - role: WorkspaceRole; + /** Leave role null to revoke access entirely */ + role?: InputMaybe; userId: Scalars['String']['input']; workspaceId: Scalars['String']['input']; }; diff --git a/packages/server/modules/workspaces/events/eventListener.ts b/packages/server/modules/workspaces/events/eventListener.ts index aa14c337e..07110aed3 100644 --- a/packages/server/modules/workspaces/events/eventListener.ts +++ b/packages/server/modules/workspaces/events/eventListener.ts @@ -21,7 +21,7 @@ import { resolveTarget } from '@/modules/serverinvites/helpers/core' import { logger } from '@/logging/logging' -import { setWorkspaceRoleFactory } from '@/modules/workspaces/services/management' +import { updateWorkspaceRoleFactory } from '@/modules/workspaces/services/management' import { getEventBus } from '@/modules/shared/services/eventBus' export const onProjectCreatedFactory = @@ -56,7 +56,7 @@ export const onInviteFinalizedFactory = (deps: { getStream: typeof getStream logger: typeof logger - setWorkspaceRole: ReturnType + updateWorkspaceRole: ReturnType }) => async ( payload: ServerInvitesEventsPayloads[typeof ServerInvitesEvents.Finalized] @@ -83,7 +83,7 @@ export const onInviteFinalizedFactory = if (!project.workspaceId) return // Add user to workspace - await deps.setWorkspaceRole({ + await deps.updateWorkspaceRole({ role: mapProjectRoleToWorkspaceRole(project.role), userId: targetUserId, workspaceId: project.workspaceId diff --git a/packages/server/modules/workspaces/graph/mocks/workspaces.ts b/packages/server/modules/workspaces/graph/mocks/workspaces.ts index f225a9bf7..1b916386a 100644 --- a/packages/server/modules/workspaces/graph/mocks/workspaces.ts +++ b/packages/server/modules/workspaces/graph/mocks/workspaces.ts @@ -62,17 +62,6 @@ const config: SpeckleModuleMocksConfig = FF_WORKSPACES_MODULE_ENABLED throw new Error('Fake update role error') } - return getMockRef('Workspace', { - id: args.input.workspaceId - }) - }, - deleteRole: (_parent, args) => { - const val = faker.datatype.boolean() - - if (val) { - throw new Error('Fake delete role error') - } - return getMockRef('Workspace', { id: args.input.workspaceId }) diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index 7c6715dcf..77dd71c65 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -1,7 +1,11 @@ import { db } from '@/db/knex' import { Resolvers } from '@/modules/core/graph/generated/graphql' import { removePrivateFields } from '@/modules/core/helpers/userHelper' -import { getStream, grantStreamPermissions } from '@/modules/core/repositories/streams' +import { + getStream, + grantStreamPermissions, + revokeStreamPermissions +} from '@/modules/core/repositories/streams' import { getUser, getUsers } from '@/modules/core/repositories/users' import { getStreams } from '@/modules/core/services/streams' import { InviteCreateValidationError } from '@/modules/serverinvites/errors' @@ -25,11 +29,14 @@ import { getFeatureFlags } from '@/modules/shared/helpers/envHelper' import { getEventBus } from '@/modules/shared/services/eventBus' import { WorkspaceInviteResourceType } from '@/modules/workspaces/domain/constants' import { + WorkspaceInvalidRoleError, WorkspaceNotFoundError, WorkspacesNotAuthorizedError, WorkspacesNotYetImplementedError } from '@/modules/workspaces/errors/workspace' +import { isWorkspaceRole } from '@/modules/workspaces/helpers/roles' import { + deleteWorkspaceRoleFactory as repoDeleteWorkspaceRoleFactory, getWorkspaceCollaboratorsFactory, getWorkspaceFactory, getWorkspaceRolesFactory, @@ -50,8 +57,9 @@ import { } from '@/modules/workspaces/services/invites' import { createWorkspaceFactory, - setWorkspaceRoleFactory, - updateWorkspaceFactory + deleteWorkspaceRoleFactory, + updateWorkspaceFactory, + updateWorkspaceRoleFactory } from '@/modules/workspaces/services/management' import { getWorkspaceProjectsFactory } from '@/modules/workspaces/services/projects' import { getWorkspacesForUserFactory } from '@/modules/workspaces/services/retrieval' @@ -120,18 +128,12 @@ export = FF_WORKSPACES_MODULE_ENABLED create: async (_parent, args, context) => { const { name, description } = args.input - const { emit: emitWorkspaceEvent } = getEventBus() - - const upsertWorkspace = upsertWorkspaceFactory({ db }) - const upsertWorkspaceRole = upsertWorkspaceRoleFactory({ db }) - // TODO: Integrate with blobstorage - const storeBlob = async () => '' - const createWorkspace = createWorkspaceFactory({ - upsertWorkspace, - upsertWorkspaceRole, - emitWorkspaceEvent, - storeBlob + upsertWorkspace: upsertWorkspaceFactory({ db }), + upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), + emitWorkspaceEvent: getEventBus().emit, + // TODO: Integrate with blobstorage + storeBlob: async () => '' }) const workspace = await createWorkspace({ @@ -154,18 +156,12 @@ export = FF_WORKSPACES_MODULE_ENABLED update: async (_parent, args, context) => { const { id: workspaceId, ...workspaceInput } = args.input - const { emit: emitWorkspaceEvent } = getEventBus() - - const getWorkspace = getWorkspaceFactory({ db }) - const upsertWorkspace = upsertWorkspaceFactory({ db }) - // TODO: Integrate with blobstorage - const storeBlob = async () => '' - const updateWorkspace = updateWorkspaceFactory({ - getWorkspace, - upsertWorkspace, - emitWorkspaceEvent, - storeBlob + getWorkspace: getWorkspaceFactory({ db }), + upsertWorkspace: upsertWorkspaceFactory({ db }), + emitWorkspaceEvent: getEventBus().emit, + // TODO: Integrate with blobstorage + storeBlob: async () => '' }) const workspace = await updateWorkspace({ @@ -177,11 +173,46 @@ export = FF_WORKSPACES_MODULE_ENABLED return workspace }, - updateRole: async () => { - throw new WorkspacesNotYetImplementedError() - }, - deleteRole: async () => { - throw new WorkspacesNotYetImplementedError() + updateRole: async (_parent, args, context) => { + const { userId, workspaceId, role } = args.input + + authorizeResolver( + context.userId, + workspaceId, + Roles.Workspace.Admin, + context.resourceAccessRules + ) + + const getWorkspaceRoles = getWorkspaceRolesFactory({ db }) + const emitWorkspaceEvent = getEventBus().emit + + if (!role) { + const deleteWorkspaceRole = deleteWorkspaceRoleFactory({ + deleteWorkspaceRole: repoDeleteWorkspaceRoleFactory({ db }), + getWorkspaceRoles, + emitWorkspaceEvent, + getStreams, + revokeStreamPermissions + }) + + await deleteWorkspaceRole(args.input) + } else { + if (!isWorkspaceRole(role)) { + throw new WorkspaceInvalidRoleError() + } + + const updateWorkspaceRole = updateWorkspaceRoleFactory({ + upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), + getWorkspaceRoles, + emitWorkspaceEvent, + getStreams, + grantStreamPermissions + }) + + await updateWorkspaceRole({ userId, workspaceId, role }) + } + + return await getWorkspaceFactory({ db })({ workspaceId }) }, invites: () => ({}) }, @@ -242,7 +273,7 @@ export = FF_WORKSPACES_MODULE_ENABLED }), processInvite: processFinalizedWorkspaceInviteFactory({ getWorkspace: getWorkspaceFactory({ db }), - setWorkspaceRole: setWorkspaceRoleFactory({ + updateWorkspaceRole: updateWorkspaceRoleFactory({ getWorkspaceRoles: getWorkspaceRolesFactory({ db }), upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), emitWorkspaceEvent: ({ eventName, payload }) => diff --git a/packages/server/modules/workspaces/helpers/roles.ts b/packages/server/modules/workspaces/helpers/roles.ts index 77ee9d406..f2cba6882 100644 --- a/packages/server/modules/workspaces/helpers/roles.ts +++ b/packages/server/modules/workspaces/helpers/roles.ts @@ -54,3 +54,7 @@ export const mapGqlWorkspaceRoleToMainRole = ( return Roles.Workspace.Guest } } + +export const isWorkspaceRole = (role: string): role is WorkspaceRoles => { + return (Object.values(Roles.Workspace) as string[]).includes(role) +} diff --git a/packages/server/modules/workspaces/index.ts b/packages/server/modules/workspaces/index.ts index 12ccf9d1e..079fcc6b4 100644 --- a/packages/server/modules/workspaces/index.ts +++ b/packages/server/modules/workspaces/index.ts @@ -12,7 +12,7 @@ import { upsertWorkspaceRoleFactory } from '@/modules/workspaces/repositories/workspaces' import { getStream, grantStreamPermissions } from '@/modules/core/repositories/streams' -import { setWorkspaceRoleFactory } from '@/modules/workspaces/services/management' +import { updateWorkspaceRoleFactory } from '@/modules/workspaces/services/management' import { getEventBus } from '@/modules/shared/services/eventBus' import { getStreams } from '@/modules/core/services/streams' @@ -39,7 +39,7 @@ const workspacesModule: SpeckleModule = { grantStreamPermissions, getStream, logger: moduleLogger, - setWorkspaceRole: setWorkspaceRoleFactory({ + updateWorkspaceRole: updateWorkspaceRoleFactory({ getWorkspaceRoles: getWorkspaceRolesFactory({ db }), upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), emitWorkspaceEvent: (...args) => getEventBus().emit(...args), diff --git a/packages/server/modules/workspaces/services/invites.ts b/packages/server/modules/workspaces/services/invites.ts index eccd907a4..d963b2577 100644 --- a/packages/server/modules/workspaces/services/invites.ts +++ b/packages/server/modules/workspaces/services/invites.ts @@ -50,7 +50,7 @@ import { WorkspaceInviteResourceType } from '@/modules/workspaces/domain/constan import { GetWorkspace } from '@/modules/workspaces/domain/operations' import { WorkspaceInviteResourceTarget } from '@/modules/workspaces/domain/types' import { mapGqlWorkspaceRoleToMainRole } from '@/modules/workspaces/helpers/roles' -import { setWorkspaceRoleFactory } from '@/modules/workspaces/services/management' +import { updateWorkspaceRoleFactory } from '@/modules/workspaces/services/management' import { PendingWorkspaceCollaboratorGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes' import { MaybeNullOrUndefined, Nullable, Roles, WorkspaceRoles } from '@speckle/shared' @@ -386,7 +386,7 @@ export const validateWorkspaceInviteBeforeFinalizationFactory = export const processFinalizedWorkspaceInviteFactory = (deps: { getWorkspace: GetWorkspace - setWorkspaceRole: ReturnType + updateWorkspaceRole: ReturnType }): ProcessFinalizedResourceInvite => async (params) => { const { invite, finalizerUserId, action } = params @@ -411,7 +411,7 @@ export const processFinalizedWorkspaceInviteFactory = const target = resolveTarget(invite.target) if (action === InviteFinalizationAction.ACCEPT) { - await deps.setWorkspaceRole({ + await deps.updateWorkspaceRole({ userId: target.userId!, workspaceId: workspace.id, role: invite.resource.role || Roles.Workspace.Member diff --git a/packages/server/modules/workspaces/services/management.ts b/packages/server/modules/workspaces/services/management.ts index 71ff88a19..ccfb0e680 100644 --- a/packages/server/modules/workspaces/services/management.ts +++ b/packages/server/modules/workspaces/services/management.ts @@ -188,8 +188,8 @@ export const deleteWorkspaceRoleFactory = revokeStreamPermissions: typeof repoRevokeStreamPermissions }) => async ({ - userId, - workspaceId + workspaceId, + userId }: WorkspaceRoleDeleteArgs): Promise => { // Protect against removing last admin const workspaceRoles = await getWorkspaceRoles({ workspaceId }) @@ -238,7 +238,7 @@ export const getWorkspaceRoleFactory = return await getWorkspaceRoleForUser({ userId, workspaceId }) } -export const setWorkspaceRoleFactory = +export const updateWorkspaceRoleFactory = ({ getWorkspaceRoles, upsertWorkspaceRole, @@ -253,7 +253,7 @@ export const setWorkspaceRoleFactory = getStreams: typeof serviceGetStreams grantStreamPermissions: typeof repoGrantStreamPermissions }) => - async ({ userId, workspaceId, role }: WorkspaceAcl): Promise => { + async ({ workspaceId, userId, role }: WorkspaceAcl): Promise => { // Protect against removing last admin const workspaceRoles = await getWorkspaceRoles({ workspaceId }) if ( diff --git a/packages/server/modules/workspaces/tests/helpers/creation.ts b/packages/server/modules/workspaces/tests/helpers/creation.ts index 3ae65d308..c2c6cfa5c 100644 --- a/packages/server/modules/workspaces/tests/helpers/creation.ts +++ b/packages/server/modules/workspaces/tests/helpers/creation.ts @@ -25,7 +25,7 @@ import { } from '@/modules/workspaces/services/invites' import { createWorkspaceFactory, - setWorkspaceRoleFactory, + updateWorkspaceRoleFactory, deleteWorkspaceRoleFactory } from '@/modules/workspaces/services/management' import { BasicTestUser } from '@/test/authHelper' @@ -76,7 +76,7 @@ export const assignToWorkspace = async ( user: BasicTestUser, role?: WorkspaceRoles ) => { - const setWorkspaceRole = setWorkspaceRoleFactory({ + const updateWorkspaceRole = updateWorkspaceRoleFactory({ getWorkspaceRoles: getWorkspaceRolesFactory({ db }), upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), emitWorkspaceEvent: (...args) => getEventBus().emit(...args), @@ -84,7 +84,7 @@ export const assignToWorkspace = async ( grantStreamPermissions }) - await setWorkspaceRole({ + await updateWorkspaceRole({ userId: user.id, workspaceId: workspace.id, role: role || Roles.Workspace.Member diff --git a/packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts new file mode 100644 index 000000000..30fb8b20a --- /dev/null +++ b/packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts @@ -0,0 +1,175 @@ +import { AllScopes } from '@/modules/core/helpers/mainConstants' +import { + BasicTestWorkspace, + createTestWorkspace +} from '@/modules/workspaces/tests/helpers/creation' +import { + BasicTestUser, + createAuthTokenForUser, + createTestUser +} from '@/test/authHelper' +import { + GetWorkspaceDocument, + UpdateWorkspaceRoleDocument +} from '@/test/graphql/generated/graphql' +import { + createTestContext, + testApolloServer, + TestApolloServer +} from '@/test/graphqlHelper' +import { beforeEachContext } from '@/test/hooks' +import { Roles } from '@speckle/shared' +import { expect } from 'chai' + +describe('Workspaces Roles GQL', () => { + let apollo: TestApolloServer + + const workspace: BasicTestWorkspace = { + id: '', + ownerId: '', + name: 'My Test Workspace' + } + + const testAdminUser: BasicTestUser = { + id: '', + name: 'John Speckle', + email: 'john-speckle-workspace-admin@example.org', + role: Roles.Server.Admin + } + + const testMemberUser: BasicTestUser = { + id: '', + name: 'James Speckle', + email: 'james-speckle-workspace-member@example.org', + role: Roles.Server.User + } + + before(async () => { + await beforeEachContext() + await Promise.all( + [testAdminUser, testMemberUser].map((user) => createTestUser(user)) + ) + const token = await createAuthTokenForUser(testAdminUser.id, AllScopes) + apollo = await testApolloServer({ + context: createTestContext({ + auth: true, + userId: testAdminUser.id, + token, + role: testAdminUser.role, + scopes: AllScopes + }) + }) + await createTestWorkspace(workspace, testAdminUser) + }) + + describe('update workspace role', () => { + after(async () => { + await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testMemberUser.id, + workspaceId: workspace.id, + role: null + } + }) + }) + + it('should create a role if none exists', async () => { + const res = await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testMemberUser.id, + workspaceId: workspace.id, + role: Roles.Workspace.Admin + } + }) + + const { data } = await apollo.execute(GetWorkspaceDocument, { + workspaceId: workspace.id + }) + const userRole = data?.workspace.team.find( + (user) => user.id === testMemberUser.id + ) + + expect(res).to.not.haveGraphQLErrors() + expect(userRole).to.exist + expect(userRole?.role).to.equal(Roles.Workspace.Admin) + }) + + it('should update a role that exists', async () => { + const res = await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testMemberUser.id, + workspaceId: workspace.id, + role: Roles.Workspace.Member + } + }) + + const roles = res.data?.workspaceMutations.updateRole.team + + expect(res).to.not.haveGraphQLErrors() + expect(roles?.some((role) => role.id === testMemberUser.id)).to.be.true + }) + + it('should throw if setting an invalid role', async () => { + const res = await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testMemberUser.id, + workspaceId: workspace.id, + role: 'not-a-role' + } + }) + + expect(res).to.haveGraphQLErrors('Invalid workspace role') + }) + + it('should throw if attempting to remove last admin', async () => { + const res = await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testAdminUser.id, + workspaceId: workspace.id, + role: Roles.Workspace.Member + } + }) + + expect(res).to.haveGraphQLErrors('last admin') + }) + }) + + describe('delete workspace role', () => { + before(async () => { + await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testMemberUser.id, + workspaceId: workspace.id, + role: Roles.Workspace.Member + } + }) + }) + + it('should delete the specified role', async () => { + const res = await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testMemberUser.id, + workspaceId: workspace.id, + role: null + } + }) + + const roles = res.data?.workspaceMutations.updateRole.team + + expect(res).to.not.haveGraphQLErrors() + expect(roles?.some((role) => role.id === testMemberUser.id)).to.be.false + }) + + it('should throw if attempting to remove last admin', async () => { + const res = await apollo.execute(UpdateWorkspaceRoleDocument, { + input: { + userId: testAdminUser.id, + workspaceId: workspace.id, + role: null + } + }) + + expect(res).to.haveGraphQLErrors('last admin') + }) + }) +}) diff --git a/packages/server/modules/workspaces/tests/unit/services/management.spec.ts b/packages/server/modules/workspaces/tests/unit/services/management.spec.ts index 533353a1e..85a4ead2d 100644 --- a/packages/server/modules/workspaces/tests/unit/services/management.spec.ts +++ b/packages/server/modules/workspaces/tests/unit/services/management.spec.ts @@ -2,7 +2,7 @@ import { Workspace, WorkspaceAcl } from '@/modules/workspacesCore/domain/types' import { createWorkspaceFactory, deleteWorkspaceRoleFactory, - setWorkspaceRoleFactory + updateWorkspaceRoleFactory } from '@/modules/workspaces/services/management' import { Roles } from '@speckle/shared' import { expect } from 'chai' @@ -199,16 +199,16 @@ const buildDeleteWorkspaceRoleAndTestContext = ( return { deleteWorkspaceRole, context } } -const buildSetWorkspaceRoleAndTestContext = ( +const buildUpdateWorkspaceRoleAndTestContext = ( contextOverrides: Partial = {}, - dependencyOverrides: Partial[0]> = {} + dependencyOverrides: Partial[0]> = {} ) => { const context = { ...getDefaultWorkspaceRoleTestContext(), ...contextOverrides } - const deps: Parameters[0] = { + const deps: Parameters[0] = { getWorkspaceRoles: async () => context.workspaceRoles, upsertWorkspaceRole: async (role) => { const currentRoleIndex = context.workspaceRoles.findIndex( @@ -255,9 +255,9 @@ const buildSetWorkspaceRoleAndTestContext = ( ...dependencyOverrides } - const setWorkspaceRole = setWorkspaceRoleFactory(deps) + const updateWorkspaceRole = updateWorkspaceRoleFactory(deps) - return { setWorkspaceRole, context } + return { updateWorkspaceRole, context } } describe('Workspace role services', () => { @@ -325,17 +325,17 @@ describe('Workspace role services', () => { }) }) - describe('setWorkspaceRoleFactory creates a function, that', () => { + describe('updateWorkspaceRoleFactory creates a function, that', () => { it('sets the workspace role', async () => { const userId = cryptoRandomString({ length: 10 }) const workspaceId = cryptoRandomString({ length: 10 }) const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Member } - const { setWorkspaceRole, context } = buildSetWorkspaceRoleAndTestContext({ + const { updateWorkspaceRole, context } = buildUpdateWorkspaceRoleAndTestContext({ workspaceId }) - await setWorkspaceRole(role) + await updateWorkspaceRole(role) expect(context.workspaceRoles.length).to.equal(1) expect(context.workspaceRoles[0]).to.deep.equal(role) @@ -345,11 +345,11 @@ describe('Workspace role services', () => { const workspaceId = cryptoRandomString({ length: 10 }) const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Member } - const { setWorkspaceRole, context } = buildSetWorkspaceRoleAndTestContext({ + const { updateWorkspaceRole, context } = buildUpdateWorkspaceRoleAndTestContext({ workspaceId }) - await setWorkspaceRole(role) + await updateWorkspaceRole(role) expect(context.eventData.isCalled).to.be.true expect(context.eventData.eventName).to.equal(WorkspaceEvents.RoleUpdated) @@ -360,13 +360,13 @@ describe('Workspace role services', () => { const workspaceId = cryptoRandomString({ length: 10 }) const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Admin } - const { setWorkspaceRole } = buildSetWorkspaceRoleAndTestContext({ + const { updateWorkspaceRole } = buildUpdateWorkspaceRoleAndTestContext({ workspaceId, workspaceRoles: [role] }) await expectToThrow(() => - setWorkspaceRole({ ...role, role: Roles.Workspace.Member }) + updateWorkspaceRole({ ...role, role: Roles.Workspace.Member }) ) }) it('sets roles on workspace projects', async () => { @@ -380,12 +380,12 @@ describe('Workspace role services', () => { role: Roles.Workspace.Admin } - const { setWorkspaceRole, context } = buildSetWorkspaceRoleAndTestContext({ + const { updateWorkspaceRole, context } = buildUpdateWorkspaceRoleAndTestContext({ workspaceId, workspaceProjects: [{ id: projectId } as StreamRecord] }) - await setWorkspaceRole(workspaceRole) + await updateWorkspaceRole(workspaceRole) expect(context.workspaceProjectRoles.length).to.equal(1) expect(context.workspaceProjectRoles[0].userId).to.equal(userId) diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 8baf85298..722423d95 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -3818,10 +3818,8 @@ export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; create: Workspace; delete: Scalars['Boolean']['output']; - deleteRole: Workspace; invites: WorkspaceInviteMutations; update: Workspace; - /** TODO: `@hasWorkspaceRole(role: WORKSPACE_ADMIN)` for role changes */ updateRole: Workspace; }; @@ -3836,11 +3834,6 @@ export type WorkspaceMutationsDeleteArgs = { }; -export type WorkspaceMutationsDeleteRoleArgs = { - input: WorkspaceRoleDeleteInput; -}; - - export type WorkspaceMutationsUpdateArgs = { input: WorkspaceUpdateInput; }; @@ -3867,7 +3860,8 @@ export type WorkspaceRoleDeleteInput = { }; export type WorkspaceRoleUpdateInput = { - role: WorkspaceRole; + /** Leave role null to revoke access entirely */ + role?: InputMaybe; userId: Scalars['String']['input']; workspaceId: Scalars['String']['input']; }; @@ -4414,7 +4408,9 @@ export type MarkProjectVersionReceivedMutation = { __typename?: 'Mutation', vers export type TestWorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logoUrl?: string | null }; -export type TestWorkspaceProjectFragment = { __typename?: 'Project', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string }; +export type TestWorkspaceTeamFragment = { __typename?: 'Workspace', team: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string }> }; + +export type TestWorkspaceProjectFragment = { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string }; export type CreateWorkspaceMutationVariables = Exact<{ input: WorkspaceCreateInput; @@ -4428,7 +4424,7 @@ export type GetWorkspaceQueryVariables = Exact<{ }>; -export type GetWorkspaceQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logoUrl?: string | null } }; +export type GetWorkspaceQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logoUrl?: string | null, team: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string }> } }; export type UpdateWorkspaceMutationVariables = Exact<{ input: WorkspaceUpdateInput; @@ -4442,12 +4438,19 @@ export type GetActiveUserWorkspacesQueryVariables = Exact<{ [key: string]: never export type GetActiveUserWorkspacesQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', workspaces: { __typename?: 'WorkspaceCollection', items: Array<{ __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logoUrl?: string | null }> } } | null }; +export type UpdateWorkspaceRoleMutationVariables = Exact<{ + input: WorkspaceRoleUpdateInput; +}>; + + +export type UpdateWorkspaceRoleMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', updateRole: { __typename?: 'Workspace', team: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string }> } } }; + export type CreateWorkspaceProjectMutationVariables = Exact<{ input: ProjectCreateInput; }>; -export type CreateWorkspaceProjectMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', create: { __typename?: 'Project', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string } } }; +export type CreateWorkspaceProjectMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', create: { __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string } } }; export type GetWorkspaceProjectsQueryVariables = Exact<{ id: Scalars['String']['input']; @@ -4457,7 +4460,7 @@ export type GetWorkspaceProjectsQueryVariables = Exact<{ }>; -export type GetWorkspaceProjectsQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', projects: { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string }> } } }; +export type GetWorkspaceProjectsQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', projects: { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string }> } } }; export const BasicWorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode; export const BasicPendingWorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; @@ -4473,7 +4476,8 @@ export const BasicStreamFieldsFragmentDoc = {"kind":"Document","definitions":[{" export const BaseUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode; export const BaseLimitedUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseLimitedUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}}]} as unknown as DocumentNode; export const TestWorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}}]}}]} as unknown as DocumentNode; -export const TestWorkspaceProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const TestWorkspaceTeamFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceTeam"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]} as unknown as DocumentNode; +export const TestWorkspaceProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const CreateWorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspaceInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; export const BatchCreateWorkspaceInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BatchCreateWorkspaceInvites"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteCreateInput"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"batchCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; export const GetWorkspaceWithTeamDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithTeam"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; @@ -4544,8 +4548,9 @@ export const RequestVerificationDocument = {"kind":"Document","definitions":[{"k export const CreateProjectVersionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProjectVersion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateVersionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}}]}}]}}]}}]} as unknown as DocumentNode; export const MarkProjectVersionReceivedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkProjectVersionReceived"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MarkReceivedVersionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markReceived"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; export const CreateWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}}]}}]} as unknown as DocumentNode; -export const GetWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}}]}}]} as unknown as DocumentNode; +export const GetWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceTeam"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceTeam"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]} as unknown as DocumentNode; export const UpdateWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}}]}}]} as unknown as DocumentNode; export const GetActiveUserWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logoUrl"}}]}}]} as unknown as DocumentNode; -export const CreateWorkspaceProjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspaceProject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceProject"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const GetWorkspaceProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceProjectsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceProject"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; \ No newline at end of file +export const UpdateWorkspaceRoleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspaceRole"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceRoleUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateRole"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateWorkspaceProjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspaceProject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceProject"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const GetWorkspaceProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceProjectsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceProject"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/packages/server/test/graphql/workspaces.ts b/packages/server/test/graphql/workspaces.ts index 73d6e4192..62487f229 100644 --- a/packages/server/test/graphql/workspaces.ts +++ b/packages/server/test/graphql/workspaces.ts @@ -11,11 +11,19 @@ export const workspaceFragment = gql` } ` +export const workspaceTeamFragment = gql` + fragment TestWorkspaceTeam on Workspace { + team { + id + role + } + } +` + export const workspaceProjectFragment = gql` fragment TestWorkspaceProject on Project { id name - description createdAt updatedAt } @@ -36,9 +44,11 @@ export const getWorkspaceQuery = gql` query GetWorkspace($workspaceId: String!) { workspace(id: $workspaceId) { ...TestWorkspace + ...TestWorkspaceTeam } } ${workspaceFragment} + ${workspaceTeamFragment} ` export const updateWorkspaceQuery = gql` @@ -65,6 +75,19 @@ export const getActiveUserWorkspacesQuery = gql` ${workspaceFragment} ` +export const updateWorkspaceRoleQuery = gql` + mutation UpdateWorkspaceRole($input: WorkspaceRoleUpdateInput!) { + workspaceMutations { + updateRole(input: $input) { + team { + id + role + } + } + } + } +` + export const createWorkspaceProjectQuery = gql` mutation CreateWorkspaceProject($input: ProjectCreateInput!) { projectMutations {