From 7105a6712412889bb526985795698278d96a5e82 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Fri, 10 Jan 2025 13:00:28 +0100 Subject: [PATCH 01/10] feat(workspaces): request to join workspace mutation --- .../typedefs/workspaces.graphql | 6 + .../modules/core/graph/generated/graphql.ts | 13 ++ .../graph/generated/graphql.ts | 10 ++ .../modules/workspaces/domain/operations.ts | 9 + .../workspaces/graph/resolvers/workspaces.ts | 33 +++- .../repositories/workspaceJoinRequests.ts | 11 +- .../services/workspaceJoinRequests.ts | 167 ++++++++++++++++-- .../integration/workspaceJoinRequests.spec.ts | 100 ++++++++++- .../server/test/graphql/generated/graphql.ts | 10 ++ 9 files changed, 343 insertions(+), 16 deletions(-) diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 8114359bec..97d360cb60 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -155,12 +155,18 @@ type WorkspaceMutations { Dismiss a workspace from the discoverable list, behind the scene a join request is created with the status "dismissed" """ dismiss(input: WorkspaceDismissInput!): Boolean! @hasServerRole(role: SERVER_USER) + requestToJoin(input: WorkspaceRequestToJoinInput!): Boolean! + @hasServerRole(role: SERVER_USER) } input WorkspaceDismissInput { workspaceId: ID! } +input WorkspaceRequestToJoinInput { + workspaceId: ID! +} + input WorkspaceCreationStateInput { workspaceId: ID! completed: Boolean! diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 2019d21b13..6be1bcac43 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -4409,6 +4409,7 @@ export type WorkspaceMutations = { join: Workspace; leave: Scalars['Boolean']['output']; projects: WorkspaceProjectMutations; + requestToJoin: Scalars['Boolean']['output']; /** Set the default region where project data will be stored. Only available to admins. */ setDefaultRegion: Workspace; update: Workspace; @@ -4457,6 +4458,11 @@ export type WorkspaceMutationsLeaveArgs = { }; +export type WorkspaceMutationsRequestToJoinArgs = { + input: WorkspaceRequestToJoinInput; +}; + + export type WorkspaceMutationsSetDefaultRegionArgs = { regionKey: Scalars['String']['input']; workspaceId: Scalars['String']['input']; @@ -4566,6 +4572,10 @@ export enum WorkspaceProjectsUpdatedMessageType { Removed = 'REMOVED' } +export type WorkspaceRequestToJoinInput = { + workspaceId: Scalars['ID']['input']; +}; + export enum WorkspaceRole { Admin = 'ADMIN', Guest = 'GUEST', @@ -5001,6 +5011,7 @@ export type ResolversTypes = { WorkspaceProjectsFilter: WorkspaceProjectsFilter; WorkspaceProjectsUpdatedMessage: ResolverTypeWrapper & { project?: Maybe }>; WorkspaceProjectsUpdatedMessageType: WorkspaceProjectsUpdatedMessageType; + WorkspaceRequestToJoinInput: WorkspaceRequestToJoinInput; WorkspaceRole: WorkspaceRole; WorkspaceRoleDeleteInput: WorkspaceRoleDeleteInput; WorkspaceRoleUpdateInput: WorkspaceRoleUpdateInput; @@ -5270,6 +5281,7 @@ export type ResolversParentTypes = { WorkspaceProjectMutations: WorkspaceProjectMutationsGraphQLReturn; WorkspaceProjectsFilter: WorkspaceProjectsFilter; WorkspaceProjectsUpdatedMessage: Omit & { project?: Maybe }; + WorkspaceRequestToJoinInput: WorkspaceRequestToJoinInput; WorkspaceRoleDeleteInput: WorkspaceRoleDeleteInput; WorkspaceRoleUpdateInput: WorkspaceRoleUpdateInput; WorkspaceSso: WorkspaceSsoGraphQLReturn; @@ -6796,6 +6808,7 @@ export type WorkspaceMutationsResolvers>; leave?: Resolver>; projects?: Resolver; + requestToJoin?: Resolver>; setDefaultRegion?: Resolver>; update?: Resolver>; updateCreationState?: 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 eb11728b8b..766e3f94d4 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -4390,6 +4390,7 @@ export type WorkspaceMutations = { join: Workspace; leave: Scalars['Boolean']['output']; projects: WorkspaceProjectMutations; + requestToJoin: Scalars['Boolean']['output']; /** Set the default region where project data will be stored. Only available to admins. */ setDefaultRegion: Workspace; update: Workspace; @@ -4438,6 +4439,11 @@ export type WorkspaceMutationsLeaveArgs = { }; +export type WorkspaceMutationsRequestToJoinArgs = { + input: WorkspaceRequestToJoinInput; +}; + + export type WorkspaceMutationsSetDefaultRegionArgs = { regionKey: Scalars['String']['input']; workspaceId: Scalars['String']['input']; @@ -4547,6 +4553,10 @@ export enum WorkspaceProjectsUpdatedMessageType { Removed = 'REMOVED' } +export type WorkspaceRequestToJoinInput = { + workspaceId: Scalars['ID']['input']; +}; + export enum WorkspaceRole { Admin = 'ADMIN', Guest = 'GUEST', diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 8ce7e2c21c..8af70fa555 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -316,3 +316,12 @@ export type UpdateWorkspaceJoinRequestStatus = (params: { userId: string status: WorkspaceJoinRequestStatus }) => Promise | undefined> + +export type CreateWorkspaceJoinRequest = ( + params: Omit +) => Promise + +export type SendWorkspaceJoinRequestReceivedEmail = (params: { + workspace: Pick + requester: { id: string; name: string; email: string } +}) => Promise diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index 64825bbe45..612231816f 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -203,8 +203,15 @@ import { Knex } from 'knex' import { getPaginatedItemsFactory } from '@/modules/shared/services/paginatedItems' import { InvalidWorkspacePlanStatus } from '@/modules/gatekeeper/errors/billing' import { BadRequestError } from '@/modules/shared/errors' -import { dismissWorkspaceJoinRequestFactory } from '@/modules/workspaces/services/workspaceJoinRequests' -import { updateWorkspaceJoinRequestStatusFactory } from '@/modules/workspaces/repositories/workspaceJoinRequests' +import { + dismissWorkspaceJoinRequestFactory, + requestToJoinWorkspaceFactory, + sendWorkspaceJoinRequestReceivedEmailFactory +} from '@/modules/workspaces/services/workspaceJoinRequests' +import { + createWorkspaceJoinRequestFactory, + updateWorkspaceJoinRequestStatusFactory +} from '@/modules/workspaces/repositories/workspaceJoinRequests' const eventBus = getEventBus() const getServerInfo = getServerInfoFactory({ db }) @@ -784,6 +791,28 @@ export = FF_WORKSPACES_MODULE_ENABLED db }) })({ userId: ctx.userId!, workspaceId: args.input.workspaceId }) + }, + requestToJoin: async (_parent, args, ctx) => { + const transaction = await db.transaction() + return await withTransaction( + requestToJoinWorkspaceFactory({ + createWorkspaceJoinRequest: createWorkspaceJoinRequestFactory({ db }), + sendWorkspaceJoinRequestReceivedEmail: + sendWorkspaceJoinRequestReceivedEmailFactory({ + renderEmail, + sendEmail, + getServerInfo, + getWorkspaceCollaborators: getWorkspaceCollaboratorsFactory({ db }), + getUserEmails: findEmailsByUserIdFactory({ db }) + }), + getUserById: getUserFactory({ db }), + getWorkspace: getWorkspaceFactory({ db }) + })({ + userId: ctx.userId!, + workspaceId: args.input.workspaceId + }), + transaction + ) } }, WorkspaceInviteMutations: { diff --git a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts index a9ab04ca50..b7ff8360bb 100644 --- a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts @@ -1,4 +1,7 @@ -import { UpdateWorkspaceJoinRequestStatus } from '@/modules/workspaces/domain/operations' +import { + CreateWorkspaceJoinRequest, + UpdateWorkspaceJoinRequestStatus +} from '@/modules/workspaces/domain/operations' import { WorkspaceJoinRequest } from '@/modules/workspacesCore/domain/types' import { WorkspaceJoinRequests } from '@/modules/workspacesCore/helpers/db' import { Knex } from 'knex' @@ -8,6 +11,12 @@ const tables = { db(WorkspaceJoinRequests.name) } +export const createWorkspaceJoinRequestFactory = + ({ db }: { db: Knex }): CreateWorkspaceJoinRequest => + async (request) => { + return await tables.workspaceJoinRequests(db).insert(request) + } + export const updateWorkspaceJoinRequestStatusFactory = ({ db }: { db: Knex }): UpdateWorkspaceJoinRequestStatus => async ({ workspaceId, userId, status }) => { diff --git a/packages/server/modules/workspaces/services/workspaceJoinRequests.ts b/packages/server/modules/workspaces/services/workspaceJoinRequests.ts index f758bc0919..4f94ccdf7f 100644 --- a/packages/server/modules/workspaces/services/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/services/workspaceJoinRequests.ts @@ -1,8 +1,18 @@ +import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' +import { GetServerInfo } from '@/modules/core/domain/server/operations' +import { FindEmailsByUserId } from '@/modules/core/domain/userEmails/operations' +import { GetUser } from '@/modules/core/domain/users/operations' +import { RenderEmail, SendEmail } from '@/modules/emails/domain/operations' +import { NotFoundError } from '@/modules/shared/errors' +import { getServerOrigin } from '@/modules/shared/helpers/envHelper' import { + CreateWorkspaceJoinRequest, GetWorkspace, + GetWorkspaceCollaborators, + SendWorkspaceJoinRequestReceivedEmail, UpdateWorkspaceJoinRequestStatus } from '@/modules/workspaces/domain/operations' -import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' +import { Roles } from '@speckle/shared' export const dismissWorkspaceJoinRequestFactory = ({ @@ -12,15 +22,150 @@ export const dismissWorkspaceJoinRequestFactory = getWorkspace: GetWorkspace updateWorkspaceJoinRequestStatus: UpdateWorkspaceJoinRequestStatus }) => - async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { - const workspace = await getWorkspace({ workspaceId }) - if (!workspace) { - throw new WorkspaceNotFoundError() + async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { + const workspace = await getWorkspace({ workspaceId }) + if (!workspace) { + throw new WorkspaceNotFoundError() + } + await updateWorkspaceJoinRequestStatus({ + userId, + workspaceId, + status: 'dismissed' + }) + return true + } + +type WorkspaceJoinRequestReceivedEmailArgs = { + workspace: { id: string; name: string; slug: string } + requester: { name: string; email: string } + workspaceAdmin: { id: string; name: string } +} + +const buildMjmlBody = ({ + workspace, + requester, + workspaceAdmin +}: WorkspaceJoinRequestReceivedEmailArgs) => { + const bodyStart = ` +Hi ${workspaceAdmin.name}! +
+
+The user ${requester.name} (${requester.email}) requested to join the workspace ${workspace.name}. You can approve or deny the request from the workspace's members settings. +
+
+ +
+ ` + const bodyEnd = ` +Have questions or feedback? Please write us at hello@speckle.systems and we'd be more than happy to talk. + ` + return { bodyStart, bodyEnd } +} + +const buildTextBody = ({ + workspace, + requester, + workspaceAdmin +}: WorkspaceJoinRequestReceivedEmailArgs) => { + const bodyStart = ` +Hi ${workspaceAdmin.name}! +\r\n\r\n +The user ${requester.name} (${requester.email}) requested to join the workspace ${workspace.name}. You can approve or deny the request from the workspace's members settings. +\r\n\r\n + ` + const bodyEnd = `Have questions or feedback? Please write us at hello@speckle.systems and we'd be more than happy to talk.` + return { bodyStart, bodyEnd } +} + +const buildEmailTemplateParams = (args: WorkspaceJoinRequestReceivedEmailArgs) => { + const url = new URL(`workspaces/${args.workspace.slug}`, getServerOrigin()).toString() + return { + mjml: buildMjmlBody(args), + text: buildTextBody(args), + cta: { + title: 'A user requested to join your workspace', + url } - await updateWorkspaceJoinRequestStatus({ - userId, - workspaceId, - status: 'dismissed' - }) - return true } +} + +export const sendWorkspaceJoinRequestReceivedEmailFactory = + ({ + renderEmail, + sendEmail, + getServerInfo, + getWorkspaceCollaborators, + getUserEmails + }: { + renderEmail: RenderEmail + sendEmail: SendEmail + getServerInfo: GetServerInfo + getWorkspaceCollaborators: GetWorkspaceCollaborators + getUserEmails: FindEmailsByUserId + }) => + async (args: Omit) => { + const [serverInfo, workspaceAdmins] = await Promise.all([ + getServerInfo(), + getWorkspaceCollaborators({ + workspaceId: args.workspace.id, + limit: 100, + filter: { roles: [Roles.Workspace.Admin] } + }) + ]) + const sendEmailParams = await Promise.all( + workspaceAdmins.map(async (admin) => { + const userEmails = await getUserEmails({ userId: admin.id }) + const emailTemplateParams = buildEmailTemplateParams({ + ...args, + workspaceAdmin: admin + }) + const { html, text } = await renderEmail(emailTemplateParams, serverInfo, null) + const subject = 'Workspace join request received' + const sendEmailParams = { + html, + text, + subject, + to: userEmails.map((e) => e.email) + } + return sendEmailParams + }) + ) + await Promise.all(sendEmailParams.map((params) => sendEmail(params))) + } + +export const requestToJoinWorkspaceFactory = + ({ + createWorkspaceJoinRequest, + sendWorkspaceJoinRequestReceivedEmail, + getUserById, + getWorkspace + }: { + createWorkspaceJoinRequest: CreateWorkspaceJoinRequest + sendWorkspaceJoinRequestReceivedEmail: SendWorkspaceJoinRequestReceivedEmail + getUserById: GetUser + getWorkspace: GetWorkspace + }) => + async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { + await createWorkspaceJoinRequest({ + userId, + workspaceId, + status: 'pending' + }) + + const requester = await getUserById(userId) + if (!requester) { + throw new NotFoundError('User not found') + } + + const workspace = await getWorkspace({ workspaceId }) + if (!workspace) { + throw new NotFoundError('Workspace not found') + } + + await sendWorkspaceJoinRequestReceivedEmail({ + workspace, + requester + }) + + return true + } diff --git a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts index 2f8b3c598d..50a9d4a959 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts @@ -2,19 +2,31 @@ import { db } from '@/db/knex' import { createRandomString } from '@/modules/core/helpers/testHelpers' import { getFeatureFlags } from '@/modules/shared/helpers/envHelper' import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' -import { updateWorkspaceJoinRequestStatusFactory } from '@/modules/workspaces/repositories/workspaceJoinRequests' import { getWorkspaceFactory } from '@/modules/workspaces/repositories/workspaces' -import { dismissWorkspaceJoinRequestFactory } from '@/modules/workspaces/services/workspaceJoinRequests' +import { UserWithOptionalRole } from '@/modules/core/repositories/users' +import { + CreateWorkspaceJoinRequest, + SendWorkspaceJoinRequestReceivedEmail +} from '@/modules/workspaces/domain/operations' +import { + dismissWorkspaceJoinRequestFactory, + requestToJoinWorkspaceFactory +} from '@/modules/workspaces/services/workspaceJoinRequests' import { BasicTestWorkspace, createTestWorkspace } from '@/modules/workspaces/tests/helpers/creation' +import { Workspace, WorkspaceJoinRequest } from '@/modules/workspacesCore/domain/types' import { WorkspaceJoinRequests } from '@/modules/workspacesCore/helpers/db' import { expectToThrow } from '@/test/assertionHelper' import { BasicTestUser, createTestUser } from '@/test/authHelper' import { Roles } from '@speckle/shared' import { expect } from 'chai' import cryptoRandomString from 'crypto-random-string' +import { + createWorkspaceJoinRequestFactory, + updateWorkspaceJoinRequestStatusFactory +} from '@/modules/workspaces/repositories/workspaceJoinRequests' const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags() @@ -70,5 +82,89 @@ const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags() ).to.deep.equal({ status: 'dismissed' }) }) }) + + describe('requestToJoinWorkspaceFactory, returns a function that ', () => { + it('throws a NotFoundError if the user does not exists', async () => { + const err = await expectToThrow(() => + requestToJoinWorkspaceFactory({ + createWorkspaceJoinRequest: (async () => + Promise.resolve()) as unknown as CreateWorkspaceJoinRequest, + sendWorkspaceJoinRequestReceivedEmail: async () => Promise.resolve(), + getUserById: async () => null, + getWorkspace: async () => null + })({ workspaceId: createRandomString(), userId: createRandomString() }) + ) + + expect(err.message).to.equal('User not found') + }) + it('throws a WorkspaceNotFoundError if the workspace does not exists', async () => { + const user = await createTestUser({}) + const err = await expectToThrow(() => + requestToJoinWorkspaceFactory({ + createWorkspaceJoinRequest: (async () => + Promise.resolve()) as unknown as CreateWorkspaceJoinRequest, + sendWorkspaceJoinRequestReceivedEmail: async () => Promise.resolve(), + getUserById: async () => user as unknown as UserWithOptionalRole, + getWorkspace: async () => null + })({ workspaceId: createRandomString(), userId: createRandomString() }) + ) + + expect(err.message).to.equal(WorkspaceNotFoundError.defaultMessage) + }) + it('creates a join request and sends an email to all admins', async () => { + const createWorkspaceJoinRequest = createWorkspaceJoinRequestFactory({ db }) + + const sendWorkspaceJoinRequestReceivedEmailCalls: Parameters[number][] = + [] + const sendWorkspaceJoinRequestReceivedEmail = async ( + args: Parameters[number] + ) => sendWorkspaceJoinRequestReceivedEmailCalls.push(args) + + const user: BasicTestUser = { + id: '', + name: 'John Speckle', + email: 'john-speckle@example.org', + role: Roles.Server.Admin, + verified: true + } + + await createTestUser(user) + + const workspace: BasicTestWorkspace = { + id: '', + slug: '', + ownerId: '', + name: cryptoRandomString({ length: 6 }), + description: cryptoRandomString({ length: 12 }) + } + await createTestWorkspace(workspace, user) + + expect( + await requestToJoinWorkspaceFactory({ + createWorkspaceJoinRequest, + sendWorkspaceJoinRequestReceivedEmail: + sendWorkspaceJoinRequestReceivedEmail as unknown as SendWorkspaceJoinRequestReceivedEmail, + getUserById: async () => user as unknown as UserWithOptionalRole, + getWorkspace: async () => workspace as unknown as Workspace + })({ workspaceId: workspace.id, userId: user.id }) + ).to.equal(true) + + expect( + (await db(WorkspaceJoinRequests.name) + .where({ + workspaceId: workspace.id, + userId: user.id + }) + .select('status') + .first())!.status + ).to.equal('pending') + + expect(sendWorkspaceJoinRequestReceivedEmailCalls).to.have.length(1) + expect(sendWorkspaceJoinRequestReceivedEmailCalls[0].workspace).to.equal( + workspace + ) + expect(sendWorkspaceJoinRequestReceivedEmailCalls[0].requester).to.equal(user) + }) + }) } ) diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 1e328b4326..e6b38b400d 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -4391,6 +4391,7 @@ export type WorkspaceMutations = { join: Workspace; leave: Scalars['Boolean']['output']; projects: WorkspaceProjectMutations; + requestToJoin: Scalars['Boolean']['output']; /** Set the default region where project data will be stored. Only available to admins. */ setDefaultRegion: Workspace; update: Workspace; @@ -4439,6 +4440,11 @@ export type WorkspaceMutationsLeaveArgs = { }; +export type WorkspaceMutationsRequestToJoinArgs = { + input: WorkspaceRequestToJoinInput; +}; + + export type WorkspaceMutationsSetDefaultRegionArgs = { regionKey: Scalars['String']['input']; workspaceId: Scalars['String']['input']; @@ -4548,6 +4554,10 @@ export enum WorkspaceProjectsUpdatedMessageType { Removed = 'REMOVED' } +export type WorkspaceRequestToJoinInput = { + workspaceId: Scalars['ID']['input']; +}; + export enum WorkspaceRole { Admin = 'ADMIN', Guest = 'GUEST', From 0762f956008205a82437c00d654907b17e33d7ce Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Sat, 11 Jan 2025 16:38:29 +0100 Subject: [PATCH 02/10] feat(workspaces): random email in test --- .../tests/integration/workspaceJoinRequests.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts index 50a9d4a959..c6a717863a 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.spec.ts @@ -1,5 +1,8 @@ import { db } from '@/db/knex' -import { createRandomString } from '@/modules/core/helpers/testHelpers' +import { + createRandomEmail, + createRandomString +} from '@/modules/core/helpers/testHelpers' import { getFeatureFlags } from '@/modules/shared/helpers/envHelper' import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' import { getWorkspaceFactory } from '@/modules/workspaces/repositories/workspaces' @@ -123,7 +126,7 @@ const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags() const user: BasicTestUser = { id: '', name: 'John Speckle', - email: 'john-speckle@example.org', + email: createRandomEmail(), role: Roles.Server.Admin, verified: true } From 0d6148aa6af42e6a79196adb71d376b8f43b4b58 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Sat, 11 Jan 2025 17:17:03 +0100 Subject: [PATCH 03/10] feat(workspaces): update email --- .../services/workspaceJoinRequests.ts | 128 +++++++++--------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/packages/server/modules/workspaces/services/workspaceJoinRequests.ts b/packages/server/modules/workspaces/services/workspaceJoinRequests.ts index 4f94ccdf7f..d4e49c340a 100644 --- a/packages/server/modules/workspaces/services/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/services/workspaceJoinRequests.ts @@ -22,22 +22,22 @@ export const dismissWorkspaceJoinRequestFactory = getWorkspace: GetWorkspace updateWorkspaceJoinRequestStatus: UpdateWorkspaceJoinRequestStatus }) => - async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { - const workspace = await getWorkspace({ workspaceId }) - if (!workspace) { - throw new WorkspaceNotFoundError() - } - await updateWorkspaceJoinRequestStatus({ - userId, - workspaceId, - status: 'dismissed' - }) - return true + async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { + const workspace = await getWorkspace({ workspaceId }) + if (!workspace) { + throw new WorkspaceNotFoundError() } + await updateWorkspaceJoinRequestStatus({ + userId, + workspaceId, + status: 'dismissed' + }) + return true + } type WorkspaceJoinRequestReceivedEmailArgs = { workspace: { id: string; name: string; slug: string } - requester: { name: string; email: string } + requester: { name: string } workspaceAdmin: { id: string; name: string } } @@ -50,7 +50,7 @@ const buildMjmlBody = ({ Hi ${workspaceAdmin.name}!

-The user ${requester.name} (${requester.email}) requested to join the workspace ${workspace.name}. You can approve or deny the request from the workspace's members settings. +${requester.name} is requesting to join your workspace ${workspace.name}.

@@ -70,7 +70,7 @@ const buildTextBody = ({ const bodyStart = ` Hi ${workspaceAdmin.name}! \r\n\r\n -The user ${requester.name} (${requester.email}) requested to join the workspace ${workspace.name}. You can approve or deny the request from the workspace's members settings. +${requester.name} is requesting to join your workspace ${workspace.name}. \r\n\r\n ` const bodyEnd = `Have questions or feedback? Please write us at hello@speckle.systems and we'd be more than happy to talk.` @@ -83,7 +83,7 @@ const buildEmailTemplateParams = (args: WorkspaceJoinRequestReceivedEmailArgs) = mjml: buildMjmlBody(args), text: buildTextBody(args), cta: { - title: 'A user requested to join your workspace', + title: 'Manage Members', url } } @@ -103,35 +103,37 @@ export const sendWorkspaceJoinRequestReceivedEmailFactory = getWorkspaceCollaborators: GetWorkspaceCollaborators getUserEmails: FindEmailsByUserId }) => - async (args: Omit) => { - const [serverInfo, workspaceAdmins] = await Promise.all([ - getServerInfo(), - getWorkspaceCollaborators({ - workspaceId: args.workspace.id, - limit: 100, - filter: { roles: [Roles.Workspace.Admin] } - }) - ]) - const sendEmailParams = await Promise.all( - workspaceAdmins.map(async (admin) => { - const userEmails = await getUserEmails({ userId: admin.id }) - const emailTemplateParams = buildEmailTemplateParams({ - ...args, - workspaceAdmin: admin - }) - const { html, text } = await renderEmail(emailTemplateParams, serverInfo, null) - const subject = 'Workspace join request received' - const sendEmailParams = { - html, - text, - subject, - to: userEmails.map((e) => e.email) - } - return sendEmailParams + async (args: Omit) => { + const { requester, workspace } = args + const [serverInfo, workspaceAdmins] = await Promise.all([ + getServerInfo(), + getWorkspaceCollaborators({ + workspaceId: workspace.id, + limit: 100, + filter: { roles: [Roles.Workspace.Admin] } + }) + ]) + const sendEmailParams = await Promise.all( + workspaceAdmins.map(async (admin) => { + const userEmails = await getUserEmails({ userId: admin.id }) + const emailTemplateParams = buildEmailTemplateParams({ + requester, + workspace, + workspaceAdmin: admin }) - ) - await Promise.all(sendEmailParams.map((params) => sendEmail(params))) - } + const { html, text } = await renderEmail(emailTemplateParams, serverInfo, null) + const subject = `${requester.name} wants to join your workspace` + const sendEmailParams = { + html, + text, + subject, + to: userEmails.map((e) => e.email) + } + return sendEmailParams + }) + ) + await Promise.all(sendEmailParams.map((params) => sendEmail(params))) + } export const requestToJoinWorkspaceFactory = ({ @@ -145,27 +147,27 @@ export const requestToJoinWorkspaceFactory = getUserById: GetUser getWorkspace: GetWorkspace }) => - async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { - await createWorkspaceJoinRequest({ - userId, - workspaceId, - status: 'pending' - }) + async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { + await createWorkspaceJoinRequest({ + userId, + workspaceId, + status: 'pending' + }) - const requester = await getUserById(userId) - if (!requester) { - throw new NotFoundError('User not found') - } + const requester = await getUserById(userId) + if (!requester) { + throw new NotFoundError('User not found') + } - const workspace = await getWorkspace({ workspaceId }) - if (!workspace) { - throw new NotFoundError('Workspace not found') - } + const workspace = await getWorkspace({ workspaceId }) + if (!workspace) { + throw new NotFoundError('Workspace not found') + } - await sendWorkspaceJoinRequestReceivedEmail({ - workspace, - requester - }) + await sendWorkspaceJoinRequestReceivedEmail({ + workspace, + requester + }) - return true - } + return true + } From fd7a8d436e8967ac89a31ac93d19e218c3358ef1 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Mon, 13 Jan 2025 19:49:41 +0100 Subject: [PATCH 04/10] feat(workspaces): code review changes --- .../modules/workspaces/domain/operations.ts | 6 ++-- .../workspaces/graph/resolvers/workspaces.ts | 29 ++++++++++++------- .../repositories/workspaceJoinRequests.ts | 7 +++-- .../services/workspaceJoinRequests.ts | 15 ++++++---- 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 8af70fa555..6d38d050ed 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -317,9 +317,9 @@ export type UpdateWorkspaceJoinRequestStatus = (params: { status: WorkspaceJoinRequestStatus }) => Promise | undefined> -export type CreateWorkspaceJoinRequest = ( - params: Omit -) => Promise +export type CreateWorkspaceJoinRequest = (params: { + workspaceJoinRequest: Omit +}) => Promise export type SendWorkspaceJoinRequestReceivedEmail = (params: { workspace: Pick diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index 612231816f..b392a16c97 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -794,19 +794,26 @@ export = FF_WORKSPACES_MODULE_ENABLED }, requestToJoin: async (_parent, args, ctx) => { const transaction = await db.transaction() + const createWorkspaceJoinRequest = createWorkspaceJoinRequestFactory({ + db: transaction + }) + const sendWorkspaceJoinRequestReceivedEmail = + sendWorkspaceJoinRequestReceivedEmailFactory({ + renderEmail, + sendEmail, + getServerInfo, + getWorkspaceCollaborators: getWorkspaceCollaboratorsFactory({ + db: transaction + }), + getUserEmails: findEmailsByUserIdFactory({ db: transaction }) + }) + return await withTransaction( requestToJoinWorkspaceFactory({ - createWorkspaceJoinRequest: createWorkspaceJoinRequestFactory({ db }), - sendWorkspaceJoinRequestReceivedEmail: - sendWorkspaceJoinRequestReceivedEmailFactory({ - renderEmail, - sendEmail, - getServerInfo, - getWorkspaceCollaborators: getWorkspaceCollaboratorsFactory({ db }), - getUserEmails: findEmailsByUserIdFactory({ db }) - }), - getUserById: getUserFactory({ db }), - getWorkspace: getWorkspaceFactory({ db }) + createWorkspaceJoinRequest, + sendWorkspaceJoinRequestReceivedEmail, + getUserById: getUserFactory({ db: transaction }), + getWorkspace: getWorkspaceFactory({ db: transaction }) })({ userId: ctx.userId!, workspaceId: args.input.workspaceId diff --git a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts index b7ff8360bb..57ab83103a 100644 --- a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts @@ -13,8 +13,11 @@ const tables = { export const createWorkspaceJoinRequestFactory = ({ db }: { db: Knex }): CreateWorkspaceJoinRequest => - async (request) => { - return await tables.workspaceJoinRequests(db).insert(request) + async ({ workspaceJoinRequest }) => { + return (await tables + .workspaceJoinRequests(db) + .insert(workspaceJoinRequest, '*') + .first()) as WorkspaceJoinRequest } export const updateWorkspaceJoinRequestStatusFactory = diff --git a/packages/server/modules/workspaces/services/workspaceJoinRequests.ts b/packages/server/modules/workspaces/services/workspaceJoinRequests.ts index d4e49c340a..43b431f607 100644 --- a/packages/server/modules/workspaces/services/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/services/workspaceJoinRequests.ts @@ -4,7 +4,7 @@ import { FindEmailsByUserId } from '@/modules/core/domain/userEmails/operations' import { GetUser } from '@/modules/core/domain/users/operations' import { RenderEmail, SendEmail } from '@/modules/emails/domain/operations' import { NotFoundError } from '@/modules/shared/errors' -import { getServerOrigin } from '@/modules/shared/helpers/envHelper' +import { getFrontendOrigin } from '@/modules/shared/helpers/envHelper' import { CreateWorkspaceJoinRequest, GetWorkspace, @@ -78,7 +78,10 @@ ${requester.name} is requesting to join your workspace ${workspace.name}. } const buildEmailTemplateParams = (args: WorkspaceJoinRequestReceivedEmailArgs) => { - const url = new URL(`workspaces/${args.workspace.slug}`, getServerOrigin()).toString() + const url = new URL( + `workspaces/${args.workspace.slug}`, + getFrontendOrigin() + ).toString() return { mjml: buildMjmlBody(args), text: buildTextBody(args), @@ -149,9 +152,11 @@ export const requestToJoinWorkspaceFactory = }) => async ({ userId, workspaceId }: { userId: string; workspaceId: string }) => { await createWorkspaceJoinRequest({ - userId, - workspaceId, - status: 'pending' + workspaceJoinRequest: { + userId, + workspaceId, + status: 'pending' + } }) const requester = await getUserById(userId) From b236f8f58ed41ca2959c23e991d69fca86454c70 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Tue, 14 Jan 2025 12:02:09 +0100 Subject: [PATCH 05/10] chore(workspaces): fix tests --- .../server/modules/core/graph/generated/graphql.ts | 1 + .../cross-server-sync/graph/generated/graphql.ts | 1 + .../server/modules/workspaces/domain/operations.ts | 2 +- .../workspaces/repositories/workspaceJoinRequests.ts | 10 +++------- packages/server/test/graphql/generated/graphql.ts | 1 + 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 6be1bcac43..fb5c4c9079 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -4404,6 +4404,7 @@ export type WorkspaceMutations = { delete: Scalars['Boolean']['output']; deleteDomain: Workspace; deleteSsoProvider: Scalars['Boolean']['output']; + /** Dismiss a workspace from the discoverable list, behind the scene a join request is created with the status "dismissed" */ dismiss: Scalars['Boolean']['output']; invites: WorkspaceInviteMutations; join: Workspace; 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 766e3f94d4..804fe3d907 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -4385,6 +4385,7 @@ export type WorkspaceMutations = { delete: Scalars['Boolean']['output']; deleteDomain: Workspace; deleteSsoProvider: Scalars['Boolean']['output']; + /** Dismiss a workspace from the discoverable list, behind the scene a join request is created with the status "dismissed" */ dismiss: Scalars['Boolean']['output']; invites: WorkspaceInviteMutations; join: Workspace; diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 6d38d050ed..f96afa8419 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -315,7 +315,7 @@ export type UpdateWorkspaceJoinRequestStatus = (params: { workspaceId: string userId: string status: WorkspaceJoinRequestStatus -}) => Promise | undefined> +}) => Promise export type CreateWorkspaceJoinRequest = (params: { workspaceJoinRequest: Omit diff --git a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts index 57ab83103a..8c3ff44a60 100644 --- a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts @@ -14,20 +14,16 @@ const tables = { export const createWorkspaceJoinRequestFactory = ({ db }: { db: Knex }): CreateWorkspaceJoinRequest => async ({ workspaceJoinRequest }) => { - return (await tables - .workspaceJoinRequests(db) - .insert(workspaceJoinRequest, '*') - .first()) as WorkspaceJoinRequest + const res = await tables.workspaceJoinRequests(db).insert(workspaceJoinRequest, '*') + return res[0] } export const updateWorkspaceJoinRequestStatusFactory = ({ db }: { db: Knex }): UpdateWorkspaceJoinRequestStatus => async ({ workspaceId, userId, status }) => { - const [request] = await tables + return await tables .workspaceJoinRequests(db) .insert({ workspaceId, userId, status }) .onConflict(['workspaceId', 'userId']) .merge(['status']) - .returning('*') - return request } diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index e6b38b400d..c31fe7bdfe 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -4386,6 +4386,7 @@ export type WorkspaceMutations = { delete: Scalars['Boolean']['output']; deleteDomain: Workspace; deleteSsoProvider: Scalars['Boolean']['output']; + /** Dismiss a workspace from the discoverable list, behind the scene a join request is created with the status "dismissed" */ dismiss: Scalars['Boolean']['output']; invites: WorkspaceInviteMutations; join: Workspace; From bb73ecbe3cbe645f18e1c977e3fb979796208290 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Mon, 13 Jan 2025 18:52:33 +0100 Subject: [PATCH 06/10] feat(workspaces): list workspace join requests for admin --- .../typedefs/workspaces.graphql | 34 ++ packages/server/codegen.yml | 4 + .../modules/core/graph/generated/graphql.ts | 366 ++++++++++-------- .../graph/generated/graphql.ts | 31 ++ .../modules/shared/services/paginatedItems.ts | 2 +- .../graph/resolvers/workspaceJoinRequests.ts | 47 +++ .../repositories/workspaceJoinRequests.ts | 62 ++- .../workspaces/tests/helpers/graphql.ts | 33 ++ .../workspaceJoinRequests.graph.spec.ts | 126 ++++++ .../workspacesCore/helpers/graphTypes.ts | 13 +- .../server/test/graphql/generated/graphql.ts | 49 +++ 11 files changed, 609 insertions(+), 158 deletions(-) create mode 100644 packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts create mode 100644 packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 97d360cb60..4eb4cc89c8 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -28,6 +28,17 @@ extend type Query { Validates the slug, to make sure it contains only valid characters and its not taken. """ validateWorkspaceSlug(slug: String!): Boolean! + + """ + Get all join requests for all the workspaces the user is an admin of + """ + adminWorkspacesJoinRequests( + filter: AdminWorkspaceJoinRequestFilter + cursor: String + limit: Int! = 25 + ): WorkspaceJoinRequestCollection! + @hasServerRole(role: SERVER_USER) + @hasScope(scope: "workspace:read") } input WorkspaceInviteLookupOptions { @@ -450,6 +461,29 @@ type WorkspaceCollection { items: [Workspace!]! } +type WorkspaceJoinRequestCollection { + totalCount: Int! + cursor: String + items: [WorkspaceJoinRequest!]! +} + +type WorkspaceJoinRequest { + workspace: Workspace! + user: LimitedUser! + status: WorkspaceJoinRequestStatus! + createdAt: DateTime! +} + +enum WorkspaceJoinRequestStatus { + pending + accepted + denied +} + +input AdminWorkspaceJoinRequestFilter { + status: WorkspaceJoinRequestStatus +} + extend type User { """ Get discoverable workspaces with verified domains that match the active user's diff --git a/packages/server/codegen.yml b/packages/server/codegen.yml index 24a7a59011..06cf776eb2 100644 --- a/packages/server/codegen.yml +++ b/packages/server/codegen.yml @@ -9,6 +9,7 @@ generates: - 'typescript-resolvers' config: contextType: '@/modules/shared/helpers/typeHelper#GraphQLContext' + allowParentTypeOverride: true mappers: Stream: '@/modules/core/helpers/graphTypes#StreamGraphQLReturn' Commit: '@/modules/core/helpers/graphTypes#CommitGraphQLReturn' @@ -64,6 +65,7 @@ generates: WorkspaceBillingMutations: '@/modules/gatekeeper/helpers/graphTypes#WorkspaceBillingMutationsGraphQLReturn' PendingWorkspaceCollaborator: '@/modules/workspacesCore/helpers/graphTypes#PendingWorkspaceCollaboratorGraphQLReturn' WorkspaceCollaborator: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceCollaboratorGraphQLReturn' + WorkspaceJoinRequest: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestGraphQLReturn' Webhook: '@/modules/webhooks/helpers/graphTypes#WebhookGraphQLReturn' SmartTextEditorValue: '@/modules/core/services/richTextEditorService#SmartTextEditorValueGraphQLReturn' BlobMetadata: '@/modules/blobstorage/domain/types#BlobStorageItem' @@ -105,6 +107,8 @@ config: scalars: JSONObject: Record DateTime: Date + enumValues: + WorkspaceJoinRequestStatus: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestStatusGraphQLReturn' require: - ts-node/register - tsconfig-paths/register diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index fb5c4c9079..028fb72321 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -1,3 +1,4 @@ +import { WorkspaceJoinRequestStatusGraphQLReturn as WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/helpers/graphTypes'; import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'; import { StreamGraphQLReturn, CommitGraphQLReturn, ProjectGraphQLReturn, ObjectGraphQLReturn, VersionGraphQLReturn, ServerInviteGraphQLReturnType, ModelGraphQLReturn, ModelsTreeItemGraphQLReturn, MutationsObjectGraphQLReturn, LimitedUserGraphQLReturn, UserGraphQLReturn, GraphQLEmptyReturn, StreamCollaboratorGraphQLReturn, ServerInfoGraphQLReturn, BranchGraphQLReturn } from '@/modules/core/helpers/graphTypes'; import { StreamAccessRequestGraphQLReturn, ProjectAccessRequestGraphQLReturn } from '@/modules/accessrequests/helpers/graphTypes'; @@ -5,7 +6,7 @@ import { CommentReplyAuthorCollectionGraphQLReturn, CommentGraphQLReturn } from import { PendingStreamCollaboratorGraphQLReturn } from '@/modules/serverinvites/helpers/graphTypes'; import { FileUploadGraphQLReturn } from '@/modules/fileuploads/helpers/types'; import { AutomateFunctionGraphQLReturn, AutomateFunctionReleaseGraphQLReturn, AutomationGraphQLReturn, AutomationRevisionGraphQLReturn, AutomationRevisionFunctionGraphQLReturn, AutomateRunGraphQLReturn, AutomationRunTriggerGraphQLReturn, AutomationRevisionTriggerDefinitionGraphQLReturn, AutomateFunctionRunGraphQLReturn, TriggeredAutomationsStatusGraphQLReturn, ProjectAutomationMutationsGraphQLReturn, ProjectTriggeredAutomationsStatusUpdatedMessageGraphQLReturn, ProjectAutomationsUpdatedMessageGraphQLReturn, UserAutomateInfoGraphQLReturn } from '@/modules/automate/helpers/graphTypes'; -import { WorkspaceGraphQLReturn, WorkspaceSsoGraphQLReturn, WorkspaceMutationsGraphQLReturn, WorkspaceInviteMutationsGraphQLReturn, WorkspaceProjectMutationsGraphQLReturn, PendingWorkspaceCollaboratorGraphQLReturn, WorkspaceCollaboratorGraphQLReturn, ProjectRoleGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes'; +import { WorkspaceGraphQLReturn, WorkspaceSsoGraphQLReturn, WorkspaceMutationsGraphQLReturn, WorkspaceInviteMutationsGraphQLReturn, WorkspaceProjectMutationsGraphQLReturn, PendingWorkspaceCollaboratorGraphQLReturn, WorkspaceCollaboratorGraphQLReturn, WorkspaceJoinRequestGraphQLReturn, ProjectRoleGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes'; import { WorkspaceBillingMutationsGraphQLReturn } from '@/modules/gatekeeper/helpers/graphTypes'; import { WebhookGraphQLReturn } from '@/modules/webhooks/helpers/graphTypes'; import { SmartTextEditorValueGraphQLReturn } from '@/modules/core/services/richTextEditorService'; @@ -24,6 +25,7 @@ export type MakeEmpty = export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; export type Omit = Pick>; export type RequireFields = Omit & { [P in K]-?: NonNullable }; +export type EnumResolverSignature = { [key in keyof T]?: AllowedValues }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: { input: string; output: string; } @@ -176,6 +178,10 @@ export type AdminUsersListItem = { registeredUser?: Maybe; }; +export type AdminWorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type ApiToken = { __typename?: 'ApiToken'; createdAt: Scalars['DateTime']['output']; @@ -2525,6 +2531,8 @@ export type Query = { * @deprecated use admin.UserList instead */ adminUsers?: Maybe; + /** Get all join requests for all the workspaces the user is an admin of */ + adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; /** Gets a specific app from the server. */ app?: Maybe; /** @@ -2652,6 +2660,13 @@ export type QueryAdminUsersArgs = { }; +export type QueryAdminWorkspacesJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + export type QueryAppArgs = { id: Scalars['String']['input']; }; @@ -4396,6 +4411,23 @@ export type WorkspaceInviteUseInput = { token: Scalars['String']['input']; }; +export type WorkspaceJoinRequest = { + __typename?: 'WorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: Workspace; +}; + +export type WorkspaceJoinRequestCollection = { + __typename?: 'WorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + +export { WorkspaceJoinRequestStatus }; + export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; addDomain: Workspace; @@ -4742,6 +4774,7 @@ export type ResolversTypes = { AdminUserListItem: ResolverTypeWrapper; AdminUsersListCollection: ResolverTypeWrapper & { items: Array }>; AdminUsersListItem: ResolverTypeWrapper & { invitedUser?: Maybe, registeredUser?: Maybe }>; + AdminWorkspaceJoinRequestFilter: AdminWorkspaceJoinRequestFilter; ApiToken: ResolverTypeWrapper; ApiTokenCreateInput: ApiTokenCreateInput; AppAuthor: ResolverTypeWrapper; @@ -5002,6 +5035,9 @@ export type ResolversTypes = { WorkspaceInviteMutations: ResolverTypeWrapper; WorkspaceInviteResendInput: WorkspaceInviteResendInput; WorkspaceInviteUseInput: WorkspaceInviteUseInput; + WorkspaceJoinRequest: ResolverTypeWrapper; + WorkspaceJoinRequestCollection: ResolverTypeWrapper & { items: Array }>; + WorkspaceJoinRequestStatus: WorkspaceJoinRequestStatusGraphQLReturn; WorkspaceMutations: ResolverTypeWrapper; WorkspacePlan: ResolverTypeWrapper; WorkspacePlanStatuses: WorkspacePlanStatuses; @@ -5040,6 +5076,7 @@ export type ResolversParentTypes = { AdminUserListItem: AdminUserListItem; AdminUsersListCollection: Omit & { items: Array }; AdminUsersListItem: Omit & { invitedUser?: Maybe, registeredUser?: Maybe }; + AdminWorkspaceJoinRequestFilter: AdminWorkspaceJoinRequestFilter; ApiToken: ApiToken; ApiTokenCreateInput: ApiTokenCreateInput; AppAuthor: AppAuthor; @@ -5275,6 +5312,8 @@ export type ResolversParentTypes = { WorkspaceInviteMutations: WorkspaceInviteMutationsGraphQLReturn; WorkspaceInviteResendInput: WorkspaceInviteResendInput; WorkspaceInviteUseInput: WorkspaceInviteUseInput; + WorkspaceJoinRequest: WorkspaceJoinRequestGraphQLReturn; + WorkspaceJoinRequestCollection: Omit & { items: Array }; WorkspaceMutations: WorkspaceMutationsGraphQLReturn; WorkspacePlan: WorkspacePlan; WorkspaceProjectCreateInput: WorkspaceProjectCreateInput; @@ -5329,14 +5368,14 @@ export type IsOwnerDirectiveArgs = { }; export type IsOwnerDirectiveResolver = DirectiveResolverFn; -export type ActiveUserMutationsResolvers = { +export type ActiveUserMutationsResolvers = { emailMutations?: Resolver; finishOnboarding?: Resolver; update?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ActivityResolvers = { +export type ActivityResolvers = { actionType?: Resolver; id?: Resolver; info?: Resolver; @@ -5349,26 +5388,26 @@ export type ActivityResolvers; }; -export type ActivityCollectionResolvers = { +export type ActivityCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminInviteListResolvers = { +export type AdminInviteListResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminMutationsResolvers = { +export type AdminMutationsResolvers = { updateWorkspacePlan?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminQueriesResolvers = { +export type AdminQueriesResolvers = { inviteList?: Resolver>; projectList?: Resolver>; serverStatistics?: Resolver; @@ -5377,14 +5416,14 @@ export type AdminQueriesResolvers; }; -export type AdminUserListResolvers = { +export type AdminUserListResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminUserListItemResolvers = { +export type AdminUserListItemResolvers = { avatar?: Resolver, ParentType, ContextType>; company?: Resolver, ParentType, ContextType>; email?: Resolver, ParentType, ContextType>; @@ -5395,20 +5434,20 @@ export type AdminUserListItemResolvers; }; -export type AdminUsersListCollectionResolvers = { +export type AdminUsersListCollectionResolvers = { items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminUsersListItemResolvers = { +export type AdminUsersListItemResolvers = { id?: Resolver; invitedUser?: Resolver, ParentType, ContextType>; registeredUser?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ApiTokenResolvers = { +export type ApiTokenResolvers = { createdAt?: Resolver; id?: Resolver; lastChars?: Resolver; @@ -5419,14 +5458,14 @@ export type ApiTokenResolvers; }; -export type AppAuthorResolvers = { +export type AppAuthorResolvers = { avatar?: Resolver, ParentType, ContextType>; id?: Resolver; name?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AuthStrategyResolvers = { +export type AuthStrategyResolvers = { color?: Resolver, ParentType, ContextType>; icon?: Resolver; id?: Resolver; @@ -5435,7 +5474,7 @@ export type AuthStrategyResolvers; }; -export type AutomateFunctionResolvers = { +export type AutomateFunctionResolvers = { creator?: Resolver, ParentType, ContextType>; description?: Resolver; id?: Resolver; @@ -5450,14 +5489,14 @@ export type AutomateFunctionResolvers; }; -export type AutomateFunctionCollectionResolvers = { +export type AutomateFunctionCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateFunctionReleaseResolvers = { +export type AutomateFunctionReleaseResolvers = { commitId?: Resolver; createdAt?: Resolver; function?: Resolver; @@ -5468,14 +5507,14 @@ export type AutomateFunctionReleaseResolvers; }; -export type AutomateFunctionReleaseCollectionResolvers = { +export type AutomateFunctionReleaseCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateFunctionRunResolvers = { +export type AutomateFunctionRunResolvers = { contextView?: Resolver, ParentType, ContextType>; createdAt?: Resolver; elapsed?: Resolver; @@ -5490,7 +5529,7 @@ export type AutomateFunctionRunResolvers; }; -export type AutomateFunctionTemplateResolvers = { +export type AutomateFunctionTemplateResolvers = { id?: Resolver; logo?: Resolver; title?: Resolver; @@ -5498,20 +5537,20 @@ export type AutomateFunctionTemplateResolvers; }; -export type AutomateFunctionTokenResolvers = { +export type AutomateFunctionTokenResolvers = { functionId?: Resolver; functionToken?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateMutationsResolvers = { +export type AutomateMutationsResolvers = { createFunction?: Resolver>; createFunctionWithoutVersion?: Resolver>; updateFunction?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateRunResolvers = { +export type AutomateRunResolvers = { automation?: Resolver; automationId?: Resolver; createdAt?: Resolver; @@ -5523,14 +5562,14 @@ export type AutomateRunResolvers; }; -export type AutomateRunCollectionResolvers = { +export type AutomateRunCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationResolvers = { +export type AutomationResolvers = { createdAt?: Resolver; creationPublicKeys?: Resolver, ParentType, ContextType>; currentRevision?: Resolver, ParentType, ContextType>; @@ -5543,35 +5582,35 @@ export type AutomationResolvers; }; -export type AutomationCollectionResolvers = { +export type AutomationCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationRevisionResolvers = { +export type AutomationRevisionResolvers = { functions?: Resolver, ParentType, ContextType>; id?: Resolver; triggerDefinitions?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationRevisionFunctionResolvers = { +export type AutomationRevisionFunctionResolvers = { parameters?: Resolver, ParentType, ContextType>; release?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationRevisionTriggerDefinitionResolvers = { +export type AutomationRevisionTriggerDefinitionResolvers = { __resolveType: TypeResolveFn<'VersionCreatedTriggerDefinition', ParentType, ContextType>; }; -export type AutomationRunTriggerResolvers = { +export type AutomationRunTriggerResolvers = { __resolveType: TypeResolveFn<'VersionCreatedTrigger', ParentType, ContextType>; }; -export type BasicGitRepositoryMetadataResolvers = { +export type BasicGitRepositoryMetadataResolvers = { id?: Resolver; name?: Resolver; owner?: Resolver; @@ -5583,7 +5622,7 @@ export interface BigIntScalarConfig extends GraphQLScalarTypeConfig = { +export type BlobMetadataResolvers = { createdAt?: Resolver; fileHash?: Resolver, ParentType, ContextType>; fileName?: Resolver; @@ -5597,7 +5636,7 @@ export type BlobMetadataResolvers; }; -export type BlobMetadataCollectionResolvers = { +export type BlobMetadataCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; @@ -5605,7 +5644,7 @@ export type BlobMetadataCollectionResolvers; }; -export type BranchResolvers = { +export type BranchResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; author?: Resolver, ParentType, ContextType>; commits?: Resolver, ParentType, ContextType, RequireFields>; @@ -5616,14 +5655,14 @@ export type BranchResolvers; }; -export type BranchCollectionResolvers = { +export type BranchCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CheckoutSessionResolvers = { +export type CheckoutSessionResolvers = { billingInterval?: Resolver; createdAt?: Resolver; id?: Resolver; @@ -5634,7 +5673,7 @@ export type CheckoutSessionResolvers; }; -export type CommentResolvers = { +export type CommentResolvers = { archived?: Resolver; author?: Resolver; authorId?: Resolver; @@ -5657,20 +5696,20 @@ export type CommentResolvers; }; -export type CommentActivityMessageResolvers = { +export type CommentActivityMessageResolvers = { comment?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommentCollectionResolvers = { +export type CommentCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommentDataFiltersResolvers = { +export type CommentDataFiltersResolvers = { hiddenIds?: Resolver>, ParentType, ContextType>; isolatedIds?: Resolver>, ParentType, ContextType>; passMax?: Resolver, ParentType, ContextType>; @@ -5680,7 +5719,7 @@ export type CommentDataFiltersResolvers; }; -export type CommentMutationsResolvers = { +export type CommentMutationsResolvers = { archive?: Resolver>; create?: Resolver>; edit?: Resolver>; @@ -5689,20 +5728,20 @@ export type CommentMutationsResolvers; }; -export type CommentReplyAuthorCollectionResolvers = { +export type CommentReplyAuthorCollectionResolvers = { items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommentThreadActivityMessageResolvers = { +export type CommentThreadActivityMessageResolvers = { data?: Resolver, ParentType, ContextType>; reply?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommitResolvers = { +export type CommitResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; authorAvatar?: Resolver, ParentType, ContextType>; authorId?: Resolver, ParentType, ContextType>; @@ -5723,14 +5762,14 @@ export type CommitResolvers; }; -export type CommitCollectionResolvers = { +export type CommitCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CountOnlyCollectionResolvers = { +export type CountOnlyCollectionResolvers = { totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; @@ -5739,7 +5778,7 @@ export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig = { +export type FileUploadResolvers = { branchName?: Resolver; convertedCommitId?: Resolver, ParentType, ContextType>; convertedLastUpdate?: Resolver; @@ -5760,7 +5799,7 @@ export type FileUploadResolvers; }; -export type GendoAiRenderResolvers = { +export type GendoAiRenderResolvers = { camera?: Resolver, ParentType, ContextType>; createdAt?: Resolver; gendoGenerationId?: Resolver, ParentType, ContextType>; @@ -5777,7 +5816,7 @@ export type GendoAiRenderResolvers; }; -export type GendoAiRenderCollectionResolvers = { +export type GendoAiRenderCollectionResolvers = { items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; @@ -5787,7 +5826,7 @@ export interface JsonObjectScalarConfig extends GraphQLScalarTypeConfig = { +export type LegacyCommentViewerDataResolvers = { camPos?: Resolver, ParentType, ContextType>; filters?: Resolver; location?: Resolver; @@ -5796,7 +5835,7 @@ export type LegacyCommentViewerDataResolvers; }; -export type LimitedUserResolvers = { +export type LimitedUserResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; avatar?: Resolver, ParentType, ContextType>; bio?: Resolver, ParentType, ContextType>; @@ -5814,7 +5853,7 @@ export type LimitedUserResolvers; }; -export type LimitedWorkspaceResolvers = { +export type LimitedWorkspaceResolvers = { defaultLogoIndex?: Resolver; description?: Resolver, ParentType, ContextType>; id?: Resolver; @@ -5824,7 +5863,7 @@ export type LimitedWorkspaceResolvers; }; -export type ModelResolvers = { +export type ModelResolvers = { author?: Resolver; automationsStatus?: Resolver, ParentType, ContextType>; childrenTree?: Resolver, ParentType, ContextType>; @@ -5842,21 +5881,21 @@ export type ModelResolvers; }; -export type ModelCollectionResolvers = { +export type ModelCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ModelMutationsResolvers = { +export type ModelMutationsResolvers = { create?: Resolver>; delete?: Resolver>; update?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ModelsTreeItemResolvers = { +export type ModelsTreeItemResolvers = { children?: Resolver, ParentType, ContextType>; fullName?: Resolver; hasChildren?: Resolver; @@ -5867,14 +5906,14 @@ export type ModelsTreeItemResolvers; }; -export type ModelsTreeItemCollectionResolvers = { +export type ModelsTreeItemCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type MutationResolvers = { +export type MutationResolvers = { _?: Resolver, ParentType, ContextType>; activeUserMutations?: Resolver; admin?: Resolver; @@ -5942,7 +5981,7 @@ export type MutationResolvers; }; -export type ObjectResolvers = { +export type ObjectResolvers = { applicationId?: Resolver, ParentType, ContextType>; children?: Resolver>; commentCount?: Resolver; @@ -5954,26 +5993,26 @@ export type ObjectResolvers; }; -export type ObjectCollectionResolvers = { +export type ObjectCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; objects?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type PasswordStrengthCheckFeedbackResolvers = { +export type PasswordStrengthCheckFeedbackResolvers = { suggestions?: Resolver, ParentType, ContextType>; warning?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type PasswordStrengthCheckResultsResolvers = { +export type PasswordStrengthCheckResultsResolvers = { feedback?: Resolver; score?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type PendingStreamCollaboratorResolvers = { +export type PendingStreamCollaboratorResolvers = { id?: Resolver; inviteId?: Resolver; invitedBy?: Resolver; @@ -5988,7 +6027,7 @@ export type PendingStreamCollaboratorResolvers; }; -export type PendingWorkspaceCollaboratorResolvers = { +export type PendingWorkspaceCollaboratorResolvers = { email?: Resolver, ParentType, ContextType>; id?: Resolver; inviteId?: Resolver; @@ -6004,7 +6043,7 @@ export type PendingWorkspaceCollaboratorResolvers; }; -export type ProjectResolvers = { +export type ProjectResolvers = { allowPublicComments?: Resolver; automation?: Resolver>; automations?: Resolver>; @@ -6039,7 +6078,7 @@ export type ProjectResolvers; }; -export type ProjectAccessRequestResolvers = { +export type ProjectAccessRequestResolvers = { createdAt?: Resolver; id?: Resolver; project?: Resolver; @@ -6049,13 +6088,13 @@ export type ProjectAccessRequestResolvers; }; -export type ProjectAccessRequestMutationsResolvers = { +export type ProjectAccessRequestMutationsResolvers = { create?: Resolver>; use?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectAutomationMutationsResolvers = { +export type ProjectAutomationMutationsResolvers = { create?: Resolver>; createRevision?: Resolver>; createTestAutomation?: Resolver>; @@ -6065,7 +6104,7 @@ export type ProjectAutomationMutationsResolvers; }; -export type ProjectAutomationsUpdatedMessageResolvers = { +export type ProjectAutomationsUpdatedMessageResolvers = { automation?: Resolver, ParentType, ContextType>; automationId?: Resolver; revision?: Resolver, ParentType, ContextType>; @@ -6073,21 +6112,21 @@ export type ProjectAutomationsUpdatedMessageResolvers; }; -export type ProjectCollaboratorResolvers = { +export type ProjectCollaboratorResolvers = { id?: Resolver; role?: Resolver; user?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectCollectionResolvers = { +export type ProjectCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectCommentCollectionResolvers = { +export type ProjectCommentCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalArchivedCount?: Resolver; @@ -6095,21 +6134,21 @@ export type ProjectCommentCollectionResolvers; }; -export type ProjectCommentsUpdatedMessageResolvers = { +export type ProjectCommentsUpdatedMessageResolvers = { comment?: Resolver, ParentType, ContextType>; id?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectFileImportUpdatedMessageResolvers = { +export type ProjectFileImportUpdatedMessageResolvers = { id?: Resolver; type?: Resolver; upload?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectInviteMutationsResolvers = { +export type ProjectInviteMutationsResolvers = { batchCreate?: Resolver>; cancel?: Resolver>; create?: Resolver>; @@ -6118,14 +6157,14 @@ export type ProjectInviteMutationsResolvers; }; -export type ProjectModelsUpdatedMessageResolvers = { +export type ProjectModelsUpdatedMessageResolvers = { id?: Resolver; model?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectMutationsResolvers = { +export type ProjectMutationsResolvers = { accessRequestMutations?: Resolver; automationMutations?: Resolver>; batchDelete?: Resolver>; @@ -6139,27 +6178,27 @@ export type ProjectMutationsResolvers; }; -export type ProjectPendingModelsUpdatedMessageResolvers = { +export type ProjectPendingModelsUpdatedMessageResolvers = { id?: Resolver; model?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectPendingVersionsUpdatedMessageResolvers = { +export type ProjectPendingVersionsUpdatedMessageResolvers = { id?: Resolver; type?: Resolver; version?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectRoleResolvers = { +export type ProjectRoleResolvers = { project?: Resolver; role?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectTriggeredAutomationsStatusUpdatedMessageResolvers = { +export type ProjectTriggeredAutomationsStatusUpdatedMessageResolvers = { model?: Resolver; project?: Resolver; run?: Resolver; @@ -6168,21 +6207,21 @@ export type ProjectTriggeredAutomationsStatusUpdatedMessageResolvers; }; -export type ProjectUpdatedMessageResolvers = { +export type ProjectUpdatedMessageResolvers = { id?: Resolver; project?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectVersionsPreviewGeneratedMessageResolvers = { +export type ProjectVersionsPreviewGeneratedMessageResolvers = { objectId?: Resolver; projectId?: Resolver; versionId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectVersionsUpdatedMessageResolvers = { +export type ProjectVersionsUpdatedMessageResolvers = { id?: Resolver; modelId?: Resolver; type?: Resolver; @@ -6190,12 +6229,13 @@ export type ProjectVersionsUpdatedMessageResolvers; }; -export type QueryResolvers = { +export type QueryResolvers = { _?: Resolver, ParentType, ContextType>; activeUser?: Resolver, ParentType, ContextType>; admin?: Resolver; adminStreams?: Resolver, ParentType, ContextType, RequireFields>; adminUsers?: Resolver, ParentType, ContextType, RequireFields>; + adminWorkspacesJoinRequests?: Resolver>; app?: Resolver, ParentType, ContextType, RequireFields>; apps?: Resolver>>, ParentType, ContextType>; authenticatedAsApp?: Resolver, ParentType, ContextType>; @@ -6229,26 +6269,26 @@ export type QueryResolvers, ParentType, ContextType, RequireFields>; }; -export type ResourceIdentifierResolvers = { +export type ResourceIdentifierResolvers = { resourceId?: Resolver; resourceType?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type RoleResolvers = { +export type RoleResolvers = { description?: Resolver; name?: Resolver; resourceTarget?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ScopeResolvers = { +export type ScopeResolvers = { description?: Resolver; name?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerAppResolvers = { +export type ServerAppResolvers = { author?: Resolver, ParentType, ContextType>; createdAt?: Resolver; description?: Resolver, ParentType, ContextType>; @@ -6264,7 +6304,7 @@ export type ServerAppResolvers; }; -export type ServerAppListItemResolvers = { +export type ServerAppListItemResolvers = { author?: Resolver, ParentType, ContextType>; description?: Resolver, ParentType, ContextType>; id?: Resolver; @@ -6276,19 +6316,19 @@ export type ServerAppListItemResolvers; }; -export type ServerAutomateInfoResolvers = { +export type ServerAutomateInfoResolvers = { availableFunctionTemplates?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerConfigurationResolvers = { +export type ServerConfigurationResolvers = { blobSizeLimitBytes?: Resolver; objectMultipartUploadSizeLimitBytes?: Resolver; objectSizeLimitBytes?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerInfoResolvers = { +export type ServerInfoResolvers = { adminContact?: Resolver, ParentType, ContextType>; authStrategies?: Resolver, ParentType, ContextType>; automate?: Resolver; @@ -6313,31 +6353,31 @@ export type ServerInfoResolvers; }; -export type ServerInfoMutationsResolvers = { +export type ServerInfoMutationsResolvers = { multiRegion?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerInviteResolvers = { +export type ServerInviteResolvers = { email?: Resolver; id?: Resolver; invitedBy?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerMigrationResolvers = { +export type ServerMigrationResolvers = { movedFrom?: Resolver, ParentType, ContextType>; movedTo?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerMultiRegionConfigurationResolvers = { +export type ServerMultiRegionConfigurationResolvers = { availableKeys?: Resolver, ParentType, ContextType>; regions?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerRegionItemResolvers = { +export type ServerRegionItemResolvers = { description?: Resolver, ParentType, ContextType>; id?: Resolver; key?: Resolver; @@ -6345,26 +6385,26 @@ export type ServerRegionItemResolvers; }; -export type ServerRegionMutationsResolvers = { +export type ServerRegionMutationsResolvers = { create?: Resolver>; update?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerRoleItemResolvers = { +export type ServerRoleItemResolvers = { id?: Resolver; title?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerStatisticsResolvers = { +export type ServerStatisticsResolvers = { totalPendingInvites?: Resolver; totalProjectCount?: Resolver; totalUserCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerStatsResolvers = { +export type ServerStatsResolvers = { commitHistory?: Resolver>>, ParentType, ContextType>; objectHistory?: Resolver>>, ParentType, ContextType>; streamHistory?: Resolver>>, ParentType, ContextType>; @@ -6376,12 +6416,12 @@ export type ServerStatsResolvers; }; -export type ServerWorkspacesInfoResolvers = { +export type ServerWorkspacesInfoResolvers = { workspacesEnabled?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type SmartTextEditorValueResolvers = { +export type SmartTextEditorValueResolvers = { attachments?: Resolver>, ParentType, ContextType>; doc?: Resolver, ParentType, ContextType>; type?: Resolver; @@ -6389,7 +6429,7 @@ export type SmartTextEditorValueResolvers; }; -export type StreamResolvers = { +export type StreamResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; allowPublicComments?: Resolver; blob?: Resolver, ParentType, ContextType, RequireFields>; @@ -6420,7 +6460,7 @@ export type StreamResolvers; }; -export type StreamAccessRequestResolvers = { +export type StreamAccessRequestResolvers = { createdAt?: Resolver; id?: Resolver; requester?: Resolver; @@ -6430,7 +6470,7 @@ export type StreamAccessRequestResolvers; }; -export type StreamCollaboratorResolvers = { +export type StreamCollaboratorResolvers = { avatar?: Resolver, ParentType, ContextType>; company?: Resolver, ParentType, ContextType>; id?: Resolver; @@ -6440,14 +6480,14 @@ export type StreamCollaboratorResolvers; }; -export type StreamCollectionResolvers = { +export type StreamCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type SubscriptionResolvers = { +export type SubscriptionResolvers = { _?: SubscriptionResolver, "_", ParentType, ContextType>; branchCreated?: SubscriptionResolver, "branchCreated", ParentType, ContextType, RequireFields>; branchDeleted?: SubscriptionResolver, "branchDeleted", ParentType, ContextType, RequireFields>; @@ -6481,32 +6521,32 @@ export type SubscriptionResolvers>; }; -export type TestAutomationRunResolvers = { +export type TestAutomationRunResolvers = { automationRunId?: Resolver; functionRunId?: Resolver; triggers?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type TestAutomationRunTriggerResolvers = { +export type TestAutomationRunTriggerResolvers = { payload?: Resolver; triggerType?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type TestAutomationRunTriggerPayloadResolvers = { +export type TestAutomationRunTriggerPayloadResolvers = { modelId?: Resolver; versionId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type TokenResourceIdentifierResolvers = { +export type TokenResourceIdentifierResolvers = { id?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type TriggeredAutomationsStatusResolvers = { +export type TriggeredAutomationsStatusResolvers = { automationRuns?: Resolver, ParentType, ContextType>; id?: Resolver; status?: Resolver; @@ -6514,7 +6554,7 @@ export type TriggeredAutomationsStatusResolvers; }; -export type UserResolvers = { +export type UserResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; apiTokens?: Resolver, ParentType, ContextType>; authorizedApps?: Resolver>, ParentType, ContextType>; @@ -6552,13 +6592,13 @@ export type UserResolvers; }; -export type UserAutomateInfoResolvers = { +export type UserAutomateInfoResolvers = { availableGithubOrgs?: Resolver, ParentType, ContextType>; hasAutomateGithubApp?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserEmailResolvers = { +export type UserEmailResolvers = { email?: Resolver; id?: Resolver; primary?: Resolver; @@ -6567,7 +6607,7 @@ export type UserEmailResolvers; }; -export type UserEmailMutationsResolvers = { +export type UserEmailMutationsResolvers = { create?: Resolver>; delete?: Resolver>; requestNewEmailVerification?: Resolver, ParentType, ContextType, RequireFields>; @@ -6575,14 +6615,14 @@ export type UserEmailMutationsResolvers; }; -export type UserGendoAiCreditsResolvers = { +export type UserGendoAiCreditsResolvers = { limit?: Resolver; resetDate?: Resolver; used?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserProjectCollectionResolvers = { +export type UserProjectCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; numberOfHidden?: Resolver; @@ -6590,20 +6630,20 @@ export type UserProjectCollectionResolvers; }; -export type UserProjectsUpdatedMessageResolvers = { +export type UserProjectsUpdatedMessageResolvers = { id?: Resolver; project?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserSearchResultCollectionResolvers = { +export type UserSearchResultCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserStreamCollectionResolvers = { +export type UserStreamCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; numberOfHidden?: Resolver; @@ -6611,7 +6651,7 @@ export type UserStreamCollectionResolvers; }; -export type VersionResolvers = { +export type VersionResolvers = { authorUser?: Resolver, ParentType, ContextType>; automationsStatus?: Resolver, ParentType, ContextType>; commentThreads?: Resolver>; @@ -6629,27 +6669,27 @@ export type VersionResolvers; }; -export type VersionCollectionResolvers = { +export type VersionCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type VersionCreatedTriggerResolvers = { +export type VersionCreatedTriggerResolvers = { model?: Resolver, ParentType, ContextType>; type?: Resolver; version?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type VersionCreatedTriggerDefinitionResolvers = { +export type VersionCreatedTriggerDefinitionResolvers = { model?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type VersionMutationsResolvers = { +export type VersionMutationsResolvers = { create?: Resolver>; delete?: Resolver>; markReceived?: Resolver>; @@ -6659,20 +6699,20 @@ export type VersionMutationsResolvers; }; -export type ViewerResourceGroupResolvers = { +export type ViewerResourceGroupResolvers = { identifier?: Resolver; items?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ViewerResourceItemResolvers = { +export type ViewerResourceItemResolvers = { modelId?: Resolver, ParentType, ContextType>; objectId?: Resolver; versionId?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ViewerUserActivityMessageResolvers = { +export type ViewerUserActivityMessageResolvers = { sessionId?: Resolver; state?: Resolver, ParentType, ContextType>; status?: Resolver; @@ -6682,7 +6722,7 @@ export type ViewerUserActivityMessageResolvers; }; -export type WebhookResolvers = { +export type WebhookResolvers = { description?: Resolver, ParentType, ContextType>; enabled?: Resolver, ParentType, ContextType>; hasSecret?: Resolver; @@ -6695,13 +6735,13 @@ export type WebhookResolvers; }; -export type WebhookCollectionResolvers = { +export type WebhookCollectionResolvers = { items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WebhookEventResolvers = { +export type WebhookEventResolvers = { id?: Resolver; lastUpdate?: Resolver; payload?: Resolver; @@ -6712,13 +6752,13 @@ export type WebhookEventResolvers; }; -export type WebhookEventCollectionResolvers = { +export type WebhookEventCollectionResolvers = { items?: Resolver>>, ParentType, ContextType>; totalCount?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceResolvers = { +export type WorkspaceResolvers = { automateFunctions?: Resolver>; createdAt?: Resolver; creationState?: Resolver, ParentType, ContextType>; @@ -6747,14 +6787,14 @@ export type WorkspaceResolvers; }; -export type WorkspaceBillingMutationsResolvers = { +export type WorkspaceBillingMutationsResolvers = { cancelCheckoutSession?: Resolver>; createCheckoutSession?: Resolver>; upgradePlan?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceCollaboratorResolvers = { +export type WorkspaceCollaboratorResolvers = { id?: Resolver; projectRoles?: Resolver, ParentType, ContextType>; role?: Resolver; @@ -6762,33 +6802,33 @@ export type WorkspaceCollaboratorResolvers; }; -export type WorkspaceCollaboratorCollectionResolvers = { +export type WorkspaceCollaboratorCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceCollectionResolvers = { +export type WorkspaceCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceCreationStateResolvers = { +export type WorkspaceCreationStateResolvers = { completed?: Resolver; state?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceDomainResolvers = { +export type WorkspaceDomainResolvers = { domain?: Resolver; id?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceInviteMutationsResolvers = { +export type WorkspaceInviteMutationsResolvers = { batchCreate?: Resolver>; cancel?: Resolver>; create?: Resolver>; @@ -6797,7 +6837,24 @@ export type WorkspaceInviteMutationsResolvers; }; -export type WorkspaceMutationsResolvers = { +export type WorkspaceJoinRequestResolvers = { + createdAt?: Resolver; + status?: Resolver; + user?: Resolver; + workspace?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type WorkspaceJoinRequestCollectionResolvers = { + cursor?: Resolver, ParentType, ContextType>; + items?: Resolver, ParentType, ContextType>; + totalCount?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type WorkspaceJoinRequestStatusResolvers = EnumResolverSignature<{ accepted?: any, denied?: any, pending?: any }, ResolversTypes['WorkspaceJoinRequestStatus']>; + +export type WorkspaceMutationsResolvers = { addDomain?: Resolver>; billing?: Resolver; create?: Resolver>; @@ -6817,21 +6874,21 @@ export type WorkspaceMutationsResolvers; }; -export type WorkspacePlanResolvers = { +export type WorkspacePlanResolvers = { createdAt?: Resolver; name?: Resolver; status?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceProjectMutationsResolvers = { +export type WorkspaceProjectMutationsResolvers = { create?: Resolver>; moveToWorkspace?: Resolver>; updateRole?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceProjectsUpdatedMessageResolvers = { +export type WorkspaceProjectsUpdatedMessageResolvers = { project?: Resolver, ParentType, ContextType>; projectId?: Resolver; type?: Resolver; @@ -6839,13 +6896,13 @@ export type WorkspaceProjectsUpdatedMessageResolvers; }; -export type WorkspaceSsoResolvers = { +export type WorkspaceSsoResolvers = { provider?: Resolver, ParentType, ContextType>; session?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceSsoProviderResolvers = { +export type WorkspaceSsoProviderResolvers = { clientId?: Resolver; id?: Resolver; issuerUrl?: Resolver; @@ -6853,13 +6910,13 @@ export type WorkspaceSsoProviderResolvers; }; -export type WorkspaceSsoSessionResolvers = { +export type WorkspaceSsoSessionResolvers = { createdAt?: Resolver; validUntil?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceSubscriptionResolvers = { +export type WorkspaceSubscriptionResolvers = { billingInterval?: Resolver; createdAt?: Resolver; currentBillingCycleEnd?: Resolver; @@ -6868,13 +6925,13 @@ export type WorkspaceSubscriptionResolvers; }; -export type WorkspaceSubscriptionSeatsResolvers = { +export type WorkspaceSubscriptionSeatsResolvers = { guest?: Resolver; plan?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceUpdatedMessageResolvers = { +export type WorkspaceUpdatedMessageResolvers = { id?: Resolver; workspace?: Resolver; __isTypeOf?: IsTypeOfResolverFn; @@ -7026,6 +7083,9 @@ export type Resolvers = { WorkspaceCreationState?: WorkspaceCreationStateResolvers; WorkspaceDomain?: WorkspaceDomainResolvers; WorkspaceInviteMutations?: WorkspaceInviteMutationsResolvers; + WorkspaceJoinRequest?: WorkspaceJoinRequestResolvers; + WorkspaceJoinRequestCollection?: WorkspaceJoinRequestCollectionResolvers; + WorkspaceJoinRequestStatus?: WorkspaceJoinRequestStatusResolvers; WorkspaceMutations?: WorkspaceMutationsResolvers; WorkspacePlan?: WorkspacePlanResolvers; WorkspaceProjectMutations?: WorkspaceProjectMutationsResolvers; 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 804fe3d907..38886cbe82 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -1,3 +1,4 @@ +import { WorkspaceJoinRequestStatusGraphQLReturn as WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/helpers/graphTypes'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -157,6 +158,10 @@ export type AdminUsersListItem = { registeredUser?: Maybe; }; +export type AdminWorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type ApiToken = { __typename?: 'ApiToken'; createdAt: Scalars['DateTime']['output']; @@ -2506,6 +2511,8 @@ export type Query = { * @deprecated use admin.UserList instead */ adminUsers?: Maybe; + /** Get all join requests for all the workspaces the user is an admin of */ + adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; /** Gets a specific app from the server. */ app?: Maybe; /** @@ -2633,6 +2640,13 @@ export type QueryAdminUsersArgs = { }; +export type QueryAdminWorkspacesJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + export type QueryAppArgs = { id: Scalars['String']['input']; }; @@ -4377,6 +4391,23 @@ export type WorkspaceInviteUseInput = { token: Scalars['String']['input']; }; +export type WorkspaceJoinRequest = { + __typename?: 'WorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: Workspace; +}; + +export type WorkspaceJoinRequestCollection = { + __typename?: 'WorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + +export { WorkspaceJoinRequestStatus }; + export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; addDomain: Workspace; diff --git a/packages/server/modules/shared/services/paginatedItems.ts b/packages/server/modules/shared/services/paginatedItems.ts index 7a0f902758..4960b46d53 100644 --- a/packages/server/modules/shared/services/paginatedItems.ts +++ b/packages/server/modules/shared/services/paginatedItems.ts @@ -20,7 +20,7 @@ export const getPaginatedItemsFactory = getTotalCount }: { getItems: (args: TArgs) => Promise - getTotalCount: (args: TArgs) => Promise + getTotalCount: (args: Omit) => Promise }) => async (args: TArgs): Promise> => { const maybeDecodedCursor = args.cursor ? decodeIsoDateCursor(args.cursor) : null diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts new file mode 100644 index 0000000000..8f51b0edea --- /dev/null +++ b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts @@ -0,0 +1,47 @@ +import { db } from '@/db/knex' +import { Resolvers } from '@/modules/core/graph/generated/graphql' +import { getPaginatedItemsFactory } from '@/modules/shared/services/paginatedItems' +import { + countAdminWorkspaceJoinRequestsFactory, + getAdminWorkspaceJoinRequestsFactory +} from '@/modules/workspaces/repositories/workspaceJoinRequests' +import { WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/domain/types' +import { WorkspaceJoinRequestGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes' + +export default { + Query: { + adminWorkspacesJoinRequests: async (_parent, args, ctx) => { + const { filter, cursor, limit } = args + + return await getPaginatedItemsFactory< + { + limit: number + cursor?: string + filter: { + userId: string + status?: WorkspaceJoinRequestStatus | null + } + }, + WorkspaceJoinRequestGraphQLReturn + >({ + getItems: getAdminWorkspaceJoinRequestsFactory({ db }), + getTotalCount: countAdminWorkspaceJoinRequestsFactory({ db }) + })({ + filter: { + status: filter?.status ?? undefined, + userId: ctx.userId! + }, + cursor: cursor ?? undefined, + limit + }) + } + }, + WorkspaceJoinRequest: { + user: async (parent, _args, ctx) => { + return await ctx.loaders.users.getUser.load(parent.userId) + }, + workspace: async (parent, _args, ctx) => { + return await ctx.loaders.workspaces!.getWorkspace.load(parent.workspaceId) + } + } +} as Resolvers diff --git a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts index 8c3ff44a60..bd6effa5e8 100644 --- a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts @@ -2,8 +2,15 @@ import { CreateWorkspaceJoinRequest, UpdateWorkspaceJoinRequestStatus } from '@/modules/workspaces/domain/operations' -import { WorkspaceJoinRequest } from '@/modules/workspacesCore/domain/types' -import { WorkspaceJoinRequests } from '@/modules/workspacesCore/helpers/db' +import { + WorkspaceJoinRequest, + WorkspaceJoinRequestStatus +} from '@/modules/workspacesCore/domain/types' +import { + WorkspaceAcl, + WorkspaceJoinRequests +} from '@/modules/workspacesCore/helpers/db' +import { Roles } from '@speckle/shared' import { Knex } from 'knex' const tables = { @@ -27,3 +34,54 @@ export const updateWorkspaceJoinRequestStatusFactory = .onConflict(['workspaceId', 'userId']) .merge(['status']) } + +type WorkspaceJoinRequestFilter = { + status?: WorkspaceJoinRequestStatus | null + userId: string +} + +const adminWorkspaceJoinRequestsBaseQueryFactory = + (db: Knex) => (filter: WorkspaceJoinRequestFilter) => { + const query = tables + .workspaceJoinRequests(db) + .innerJoin( + WorkspaceAcl.name, + WorkspaceAcl.col.workspaceId, + WorkspaceJoinRequests.col.workspaceId + ) + .where(WorkspaceAcl.col.role, Roles.Workspace.Admin) + .where(WorkspaceAcl.col.userId, filter.userId) + if (filter.status) query.andWhere(WorkspaceJoinRequests.col.status, filter.status) + return query + } + +export const getAdminWorkspaceJoinRequestsFactory = + ({ db }: { db: Knex }) => + async ({ + filter, + cursor, + limit + }: { + filter: WorkspaceJoinRequestFilter + cursor?: string + limit: number + }) => { + const query = adminWorkspaceJoinRequestsBaseQueryFactory(db)(filter) + + if (cursor) { + query.andWhere(WorkspaceJoinRequests.col.createdAt, '<', cursor) + } + return await query + .select(WorkspaceJoinRequests.cols) + .orderBy(WorkspaceJoinRequests.col.createdAt, 'desc') + .limit(limit) + } + +export const countAdminWorkspaceJoinRequestsFactory = + ({ db }: { db: Knex }) => + async ({ filter }: { filter: WorkspaceJoinRequestFilter }) => { + const query = adminWorkspaceJoinRequestsBaseQueryFactory(db)(filter) + + const [res] = await query.count() + return parseInt(res.count.toString()) + } diff --git a/packages/server/modules/workspaces/tests/helpers/graphql.ts b/packages/server/modules/workspaces/tests/helpers/graphql.ts index f6a6266775..653a45b9f3 100644 --- a/packages/server/modules/workspaces/tests/helpers/graphql.ts +++ b/packages/server/modules/workspaces/tests/helpers/graphql.ts @@ -323,3 +323,36 @@ export const dismissWorkspaceMutation = gql` } } ` + +export const requestToJoinWorkspaceMutation = gql` + mutation requestToJoinWorkspace($input: WorkspaceRequestToJoinInput!) { + workspaceMutations { + requestToJoin(input: $input) + } + } +` + +export const adminWorkspaceJoinRequestsQuery = gql` + query adminWorkspaceJoinRequests( + $filter: AdminWorkspaceJoinRequestFilter + $cursor: String + $limit: Int + ) { + adminWorkspacesJoinRequests(filter: $filter, cursor: $cursor, limit: $limit) { + items { + status + user { + id + name + } + workspace { + id + name + } + createdAt + } + cursor + totalCount + } + } +` diff --git a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts new file mode 100644 index 0000000000..adab67f49f --- /dev/null +++ b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts @@ -0,0 +1,126 @@ +import { db } from '@/db/knex' +import { createRandomString } from '@/modules/core/helpers/testHelpers' +import { createTestWorkspace } from '@/modules/workspaces/tests/helpers/creation' +import { + BasicTestUser, + createAuthTokenForUser, + createTestUser +} from '@/test/authHelper' +import { + AdminWorkspaceJoinRequestsDocument, + RequestToJoinWorkspaceDocument +} from '@/test/graphql/generated/graphql' +import { createTestContext, testApolloServer } from '@/test/graphqlHelper' +import { beforeEachContext } from '@/test/hooks' +import { AllScopes, Roles } from '@speckle/shared' +import { assert, expect } from 'chai' +import { upsertWorkspaceRoleFactory } from '@/modules/workspaces/repositories/workspaces' + +async function login(user: BasicTestUser) { + const token = await createAuthTokenForUser(user.id, AllScopes) + return await testApolloServer({ + context: await createTestContext({ + auth: true, + userId: user.id, + token, + role: user.role, + scopes: AllScopes + }) + }) +} + +before(async () => { + await beforeEachContext() +}) + +describe('WorkspaceJoinRequests GQL', () => { + describe('User.adminWorkspacesJoinRequests', () => { + it('should return the workspace join requests for the admin', async () => { + const admin = await createTestUser({ + name: 'admin user', + role: Roles.Server.User + }) + + const user1 = await createTestUser({ name: 'user 1', role: Roles.Server.User }) + const user2 = await createTestUser({ name: 'user 2', role: Roles.Server.User }) + + const workspace1 = { + id: createRandomString(), + name: 'Workspace 1', + ownerId: admin.id, + description: '' + } + await createTestWorkspace(workspace1, admin) + + const workspace2 = { + id: createRandomString(), + name: 'Workspace 2', + ownerId: admin.id, + description: '' + } + await createTestWorkspace(workspace2, admin) + + const nobodyWorkspace = { + id: createRandomString(), + name: 'nobody', + ownerId: admin.id, + description: '' + } + await createTestWorkspace(nobodyWorkspace, admin) + + const nonAdminWorkspace = { + id: createRandomString(), + name: 'nonadmin', + ownerId: admin.id, + description: '' + } + await createTestWorkspace(nonAdminWorkspace, admin) + await upsertWorkspaceRoleFactory({ db })({ + userId: admin.id, + workspaceId: nonAdminWorkspace.id, + role: Roles.Workspace.Member, + createdAt: new Date() + }) + + // User1 requests to join workspace1 + const sessionUser1 = await login(user1) + const joinReq1 = await sessionUser1.execute(RequestToJoinWorkspaceDocument, { + input: { + workspaceId: workspace1.id + } + }) + expect(joinReq1).to.not.haveGraphQLErrors() + + // User2 requests to join workspace2 + const sessionUser2 = await login(user2) + const joinReq2 = await sessionUser2.execute(RequestToJoinWorkspaceDocument, { + input: { + workspaceId: workspace2.id + } + }) + expect(joinReq2).to.not.haveGraphQLErrors() + + const sessionAdmin = await login(admin) + const res = await sessionAdmin.execute(AdminWorkspaceJoinRequestsDocument, {}) + expect(res).to.not.haveGraphQLErrors() + + const { items, totalCount } = res.data?.adminWorkspacesJoinRequests + + expect(totalCount).to.equal(2) + + expect(items).to.have.length(2) + assert.deepEqual(items[1], { + status: 'pending', + user: { id: user1.id, name: user1.name }, + workspace: { id: workspace1.id, name: workspace1.name }, + createdAt: items[1].createdAt + }) + assert.deepEqual(items[0], { + status: 'pending', + user: { id: user2.id, name: user2.name }, + workspace: { id: workspace2.id, name: workspace2.name }, + createdAt: items[0].createdAt + }) + }) + }) +}) diff --git a/packages/server/modules/workspacesCore/helpers/graphTypes.ts b/packages/server/modules/workspacesCore/helpers/graphTypes.ts index 34cc0f81d3..0fab0f1644 100644 --- a/packages/server/modules/workspacesCore/helpers/graphTypes.ts +++ b/packages/server/modules/workspacesCore/helpers/graphTypes.ts @@ -1,11 +1,20 @@ import { MutationsObjectGraphQLReturn } from '@/modules/core/helpers/graphTypes' -import { LimitedUserRecord } from '@/modules/core/helpers/types' +import { LimitedUserRecord, UserRecord } from '@/modules/core/helpers/types' import { WorkspaceSsoProviderRecord } from '@/modules/workspaces/domain/sso/types' import { WorkspaceTeamMember } from '@/modules/workspaces/domain/types' -import { Workspace } from '@/modules/workspacesCore/domain/types' +import { + Workspace, + WorkspaceJoinRequest, + WorkspaceJoinRequestStatus +} from '@/modules/workspacesCore/domain/types' import { WorkspaceRoles } from '@speckle/shared' export type WorkspaceGraphQLReturn = Workspace +export type WorkspaceJoinRequestGraphQLReturn = WorkspaceJoinRequest & { + user: UserRecord + workspace: Workspace +} +export type WorkspaceJoinRequestStatusGraphQLReturn = WorkspaceJoinRequestStatus export type WorkspaceBillingGraphQLReturn = { parent: Workspace } export type WorkspaceSsoGraphQLReturn = WorkspaceSsoProviderRecord export type WorkspaceMutationsGraphQLReturn = MutationsObjectGraphQLReturn diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index c31fe7bdfe..0a2fe9fa3c 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -1,3 +1,4 @@ +import { WorkspaceJoinRequestStatusGraphQLReturn as WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/helpers/graphTypes'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; export type Maybe = T | null; export type InputMaybe = Maybe; @@ -158,6 +159,10 @@ export type AdminUsersListItem = { registeredUser?: Maybe; }; +export type AdminWorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type ApiToken = { __typename?: 'ApiToken'; createdAt: Scalars['DateTime']['output']; @@ -2507,6 +2512,8 @@ export type Query = { * @deprecated use admin.UserList instead */ adminUsers?: Maybe; + /** Get all join requests for all the workspaces the user is an admin of */ + adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; /** Gets a specific app from the server. */ app?: Maybe; /** @@ -2634,6 +2641,13 @@ export type QueryAdminUsersArgs = { }; +export type QueryAdminWorkspacesJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + export type QueryAppArgs = { id: Scalars['String']['input']; }; @@ -4378,6 +4392,23 @@ export type WorkspaceInviteUseInput = { token: Scalars['String']['input']; }; +export type WorkspaceJoinRequest = { + __typename?: 'WorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: Workspace; +}; + +export type WorkspaceJoinRequestCollection = { + __typename?: 'WorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + +export { WorkspaceJoinRequestStatus }; + export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; addDomain: Workspace; @@ -4899,6 +4930,22 @@ export type DismissWorkspaceMutationVariables = Exact<{ export type DismissWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', dismiss: boolean } }; +export type RequestToJoinWorkspaceMutationVariables = Exact<{ + input: WorkspaceRequestToJoinInput; +}>; + + +export type RequestToJoinWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', requestToJoin: boolean } }; + +export type AdminWorkspaceJoinRequestsQueryVariables = Exact<{ + filter?: InputMaybe; + cursor?: InputMaybe; + limit?: InputMaybe; +}>; + + +export type AdminWorkspaceJoinRequestsQuery = { __typename?: 'Query', adminWorkspacesJoinRequests: { __typename?: 'WorkspaceJoinRequestCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, createdAt: string, user: { __typename?: 'LimitedUser', id: string, name: string }, workspace: { __typename?: 'Workspace', id: string, name: string } }> } }; + export type BasicStreamAccessRequestFieldsFragment = { __typename?: 'StreamAccessRequest', id: string, requesterId: string, streamId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name: string } }; export type CreateStreamAccessRequestMutationVariables = Exact<{ @@ -5603,6 +5650,8 @@ export const SetWorkspaceDefaultRegionDocument = {"kind":"Document","definitions export const OnWorkspaceProjectsUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnWorkspaceProjectsUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceProjectsUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"workspaceSlug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const OnWorkspaceUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnWorkspaceUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"workspaceSlug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"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":"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":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"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 DismissWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"dismissWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceDismissInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dismiss"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; +export const RequestToJoinWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"requestToJoinWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceRequestToJoinInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestToJoin"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; +export const AdminWorkspaceJoinRequestsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"adminWorkspaceJoinRequests"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AdminWorkspaceJoinRequestFilter"}}},{"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":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"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":"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":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode; export const CreateStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequestCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; export const GetStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; export const GetFullStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFullStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}},{"kind":"Field","name":{"kind":"Name","value":"stream"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; From 85ce151264bec8a6bcbb5a01259464c909bb0434 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Tue, 14 Jan 2025 11:34:02 +0100 Subject: [PATCH 07/10] feat(workspaces): make query for single workspace --- .../typedefs/workspaces.graphql | 23 +++++----- .../modules/core/graph/generated/graphql.ts | 20 ++++----- .../graph/generated/graphql.ts | 18 ++++---- .../graph/resolvers/workspaceJoinRequests.ts | 7 +-- .../repositories/workspaceJoinRequests.ts | 2 + .../workspaces/tests/helpers/graphql.ts | 35 ++++++++------- .../workspaceJoinRequests.graph.spec.ts | 43 ++++++++++++++----- .../server/test/graphql/generated/graphql.ts | 25 +++++------ 8 files changed, 102 insertions(+), 71 deletions(-) diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 4eb4cc89c8..d1c85e13a8 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -28,17 +28,6 @@ extend type Query { Validates the slug, to make sure it contains only valid characters and its not taken. """ validateWorkspaceSlug(slug: String!): Boolean! - - """ - Get all join requests for all the workspaces the user is an admin of - """ - adminWorkspacesJoinRequests( - filter: AdminWorkspaceJoinRequestFilter - cursor: String - limit: Int! = 25 - ): WorkspaceJoinRequestCollection! - @hasServerRole(role: SERVER_USER) - @hasScope(scope: "workspace:read") } input WorkspaceInviteLookupOptions { @@ -322,6 +311,18 @@ type Workspace { Info about the workspace creation state """ creationState: WorkspaceCreationState + """ + Get all join requests for all the workspaces the user is an admin of + """ + adminWorkspacesJoinRequests( + filter: AdminWorkspaceJoinRequestFilter + cursor: String + limit: Int! = 25 + ): WorkspaceJoinRequestCollection! + @hasServerRole(role: SERVER_USER) + @hasScope(scope: "workspace:read") + @hasWorkspaceRole(role: ADMIN) + @isOwner } type WorkspaceCreationState { diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 028fb72321..13c1b1adac 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -2531,8 +2531,6 @@ export type Query = { * @deprecated use admin.UserList instead */ adminUsers?: Maybe; - /** Get all join requests for all the workspaces the user is an admin of */ - adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; /** Gets a specific app from the server. */ app?: Maybe; /** @@ -2660,13 +2658,6 @@ export type QueryAdminUsersArgs = { }; -export type QueryAdminWorkspacesJoinRequestsArgs = { - cursor?: InputMaybe; - filter?: InputMaybe; - limit?: Scalars['Int']['input']; -}; - - export type QueryAppArgs = { id: Scalars['String']['input']; }; @@ -4183,6 +4174,8 @@ export type WebhookUpdateInput = { export type Workspace = { __typename?: 'Workspace'; + /** Get all join requests for all the workspaces the user is an admin of */ + adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; automateFunctions: AutomateFunctionCollection; createdAt: Scalars['DateTime']['output']; /** Info about the workspace creation state */ @@ -4226,6 +4219,13 @@ export type Workspace = { }; +export type WorkspaceAdminWorkspacesJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + export type WorkspaceAutomateFunctionsArgs = { cursor?: InputMaybe; filter?: InputMaybe; @@ -6235,7 +6235,6 @@ export type QueryResolvers; adminStreams?: Resolver, ParentType, ContextType, RequireFields>; adminUsers?: Resolver, ParentType, ContextType, RequireFields>; - adminWorkspacesJoinRequests?: Resolver>; app?: Resolver, ParentType, ContextType, RequireFields>; apps?: Resolver>>, ParentType, ContextType>; authenticatedAsApp?: Resolver, ParentType, ContextType>; @@ -6759,6 +6758,7 @@ export type WebhookEventCollectionResolvers = { + adminWorkspacesJoinRequests?: Resolver>; automateFunctions?: Resolver>; createdAt?: Resolver; creationState?: Resolver, ParentType, ContextType>; 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 38886cbe82..d724c6e81c 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -2511,8 +2511,6 @@ export type Query = { * @deprecated use admin.UserList instead */ adminUsers?: Maybe; - /** Get all join requests for all the workspaces the user is an admin of */ - adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; /** Gets a specific app from the server. */ app?: Maybe; /** @@ -2640,13 +2638,6 @@ export type QueryAdminUsersArgs = { }; -export type QueryAdminWorkspacesJoinRequestsArgs = { - cursor?: InputMaybe; - filter?: InputMaybe; - limit?: Scalars['Int']['input']; -}; - - export type QueryAppArgs = { id: Scalars['String']['input']; }; @@ -4163,6 +4154,8 @@ export type WebhookUpdateInput = { export type Workspace = { __typename?: 'Workspace'; + /** Get all join requests for all the workspaces the user is an admin of */ + adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; automateFunctions: AutomateFunctionCollection; createdAt: Scalars['DateTime']['output']; /** Info about the workspace creation state */ @@ -4206,6 +4199,13 @@ export type Workspace = { }; +export type WorkspaceAdminWorkspacesJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + export type WorkspaceAutomateFunctionsArgs = { cursor?: InputMaybe; filter?: InputMaybe; diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts index 8f51b0edea..b1654e4dac 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts @@ -9,8 +9,8 @@ import { WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/domain/type import { WorkspaceJoinRequestGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes' export default { - Query: { - adminWorkspacesJoinRequests: async (_parent, args, ctx) => { + Workspace: { + adminWorkspacesJoinRequests: async (parent, args, ctx) => { const { filter, cursor, limit } = args return await getPaginatedItemsFactory< @@ -28,8 +28,9 @@ export default { getTotalCount: countAdminWorkspaceJoinRequestsFactory({ db }) })({ filter: { + workspaceId: parent.id, status: filter?.status ?? undefined, - userId: ctx.userId! + userId: ctx.userId! // This is the worskpace admin, not the request userId }, cursor: cursor ?? undefined, limit diff --git a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts index bd6effa5e8..169abbd7ef 100644 --- a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts @@ -36,6 +36,7 @@ export const updateWorkspaceJoinRequestStatusFactory = } type WorkspaceJoinRequestFilter = { + workspaceId: string status?: WorkspaceJoinRequestStatus | null userId: string } @@ -51,6 +52,7 @@ const adminWorkspaceJoinRequestsBaseQueryFactory = ) .where(WorkspaceAcl.col.role, Roles.Workspace.Admin) .where(WorkspaceAcl.col.userId, filter.userId) + .where(WorkspaceJoinRequests.col.workspaceId, filter.workspaceId) if (filter.status) query.andWhere(WorkspaceJoinRequests.col.status, filter.status) return query } diff --git a/packages/server/modules/workspaces/tests/helpers/graphql.ts b/packages/server/modules/workspaces/tests/helpers/graphql.ts index 653a45b9f3..175ce9b600 100644 --- a/packages/server/modules/workspaces/tests/helpers/graphql.ts +++ b/packages/server/modules/workspaces/tests/helpers/graphql.ts @@ -332,27 +332,32 @@ export const requestToJoinWorkspaceMutation = gql` } ` -export const adminWorkspaceJoinRequestsQuery = gql` - query adminWorkspaceJoinRequests( +export const getWorkspaceWithJoinRequestsQuery = gql` + query GetWorkspaceWithJoinRequests( + $workspaceId: String! $filter: AdminWorkspaceJoinRequestFilter $cursor: String $limit: Int ) { - adminWorkspacesJoinRequests(filter: $filter, cursor: $cursor, limit: $limit) { - items { - status - user { - id - name - } - workspace { - id - name + workspace(id: $workspaceId) { + ...BasicWorkspace + adminWorkspacesJoinRequests(filter: $filter, cursor: $cursor, limit: $limit) { + items { + status + user { + id + name + } + workspace { + id + name + } + createdAt } - createdAt + cursor + totalCount } - cursor - totalCount } } + ${basicWorkspaceFragment} ` diff --git a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts index adab67f49f..a95db72052 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts @@ -7,7 +7,7 @@ import { createTestUser } from '@/test/authHelper' import { - AdminWorkspaceJoinRequestsDocument, + GetWorkspaceWithJoinRequestsDocument, RequestToJoinWorkspaceDocument } from '@/test/graphql/generated/graphql' import { createTestContext, testApolloServer } from '@/test/graphqlHelper' @@ -34,7 +34,7 @@ before(async () => { }) describe('WorkspaceJoinRequests GQL', () => { - describe('User.adminWorkspacesJoinRequests', () => { + describe('Workspace.adminWorkspacesJoinRequests', () => { it('should return the workspace join requests for the admin', async () => { const admin = await createTestUser({ name: 'admin user', @@ -101,25 +101,46 @@ describe('WorkspaceJoinRequests GQL', () => { expect(joinReq2).to.not.haveGraphQLErrors() const sessionAdmin = await login(admin) - const res = await sessionAdmin.execute(AdminWorkspaceJoinRequestsDocument, {}) - expect(res).to.not.haveGraphQLErrors() + const workspace1Res = await sessionAdmin.execute( + GetWorkspaceWithJoinRequestsDocument, + { + workspaceId: workspace1.id + } + ) + expect(workspace1Res).to.not.haveGraphQLErrors() - const { items, totalCount } = res.data?.adminWorkspacesJoinRequests + const { items: items1, totalCount: totalCount1 } = + workspace1Res.data!.workspace!.adminWorkspacesJoinRequests! - expect(totalCount).to.equal(2) + expect(totalCount1).to.equal(1) - expect(items).to.have.length(2) - assert.deepEqual(items[1], { + expect(items1).to.have.length(1) + assert.deepEqual(items1[0], { status: 'pending', user: { id: user1.id, name: user1.name }, workspace: { id: workspace1.id, name: workspace1.name }, - createdAt: items[1].createdAt + createdAt: items1[0].createdAt }) - assert.deepEqual(items[0], { + + const workspace2Res = await sessionAdmin.execute( + GetWorkspaceWithJoinRequestsDocument, + { + workspaceId: workspace2.id + } + ) + expect(workspace2Res).to.not.haveGraphQLErrors() + + const { items: items2, totalCount: totalCount2 } = + workspace2Res.data!.workspace!.adminWorkspacesJoinRequests! + + expect(totalCount2).to.equal(1) + + expect(items2).to.have.length(1) + assert.deepEqual(items2[0], { status: 'pending', user: { id: user2.id, name: user2.name }, workspace: { id: workspace2.id, name: workspace2.name }, - createdAt: items[0].createdAt + createdAt: items2[0].createdAt }) }) }) diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 0a2fe9fa3c..762ccf56c2 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -2512,8 +2512,6 @@ export type Query = { * @deprecated use admin.UserList instead */ adminUsers?: Maybe; - /** Get all join requests for all the workspaces the user is an admin of */ - adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; /** Gets a specific app from the server. */ app?: Maybe; /** @@ -2641,13 +2639,6 @@ export type QueryAdminUsersArgs = { }; -export type QueryAdminWorkspacesJoinRequestsArgs = { - cursor?: InputMaybe; - filter?: InputMaybe; - limit?: Scalars['Int']['input']; -}; - - export type QueryAppArgs = { id: Scalars['String']['input']; }; @@ -4164,6 +4155,8 @@ export type WebhookUpdateInput = { export type Workspace = { __typename?: 'Workspace'; + /** Get all join requests for all the workspaces the user is an admin of */ + adminWorkspacesJoinRequests: WorkspaceJoinRequestCollection; automateFunctions: AutomateFunctionCollection; createdAt: Scalars['DateTime']['output']; /** Info about the workspace creation state */ @@ -4207,6 +4200,13 @@ export type Workspace = { }; +export type WorkspaceAdminWorkspacesJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + export type WorkspaceAutomateFunctionsArgs = { cursor?: InputMaybe; filter?: InputMaybe; @@ -4937,14 +4937,15 @@ export type RequestToJoinWorkspaceMutationVariables = Exact<{ export type RequestToJoinWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', requestToJoin: boolean } }; -export type AdminWorkspaceJoinRequestsQueryVariables = Exact<{ +export type GetWorkspaceWithJoinRequestsQueryVariables = Exact<{ + workspaceId: Scalars['String']['input']; filter?: InputMaybe; cursor?: InputMaybe; limit?: InputMaybe; }>; -export type AdminWorkspaceJoinRequestsQuery = { __typename?: 'Query', adminWorkspacesJoinRequests: { __typename?: 'WorkspaceJoinRequestCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, createdAt: string, user: { __typename?: 'LimitedUser', id: string, name: string }, workspace: { __typename?: 'Workspace', id: string, name: string } }> } }; +export type GetWorkspaceWithJoinRequestsQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, readOnly: boolean, adminWorkspacesJoinRequests: { __typename?: 'WorkspaceJoinRequestCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, createdAt: string, user: { __typename?: 'LimitedUser', id: string, name: string }, workspace: { __typename?: 'Workspace', id: string, name: string } }> } } }; export type BasicStreamAccessRequestFieldsFragment = { __typename?: 'StreamAccessRequest', id: string, requesterId: string, streamId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name: string } }; @@ -5651,7 +5652,7 @@ export const OnWorkspaceProjectsUpdatedDocument = {"kind":"Document","definition export const OnWorkspaceUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnWorkspaceUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"workspaceSlug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"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":"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":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"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 DismissWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"dismissWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceDismissInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dismiss"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; export const RequestToJoinWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"requestToJoinWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceRequestToJoinInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestToJoin"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; -export const AdminWorkspaceJoinRequestsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"adminWorkspaceJoinRequests"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AdminWorkspaceJoinRequestFilter"}}},{"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":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"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":"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":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode; +export const GetWorkspaceWithJoinRequestsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithJoinRequests"},"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":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AdminWorkspaceJoinRequestFilter"}}},{"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":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}],"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":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"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":"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":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"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":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]} as unknown as DocumentNode; export const CreateStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequestCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; export const GetStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; export const GetFullStreamAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFullStreamAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"streamId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"}},{"kind":"Field","name":{"kind":"Name","value":"stream"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; From 7bf3c5df74bd24e05f18d7f2feb4e6cfc45c2e43 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Tue, 14 Jan 2025 17:58:51 +0100 Subject: [PATCH 08/10] chore(workspaces): fix graphql enum --- packages/server/codegen.yml | 3 - .../modules/core/graph/generated/graphql.ts | 321 +++++++++--------- .../graph/generated/graphql.ts | 7 +- .../graph/resolvers/workspaceJoinRequests.ts | 1 + .../repositories/workspaceJoinRequests.ts | 2 +- .../modules/workspacesCore/domain/types.ts | 6 +- .../workspacesCore/helpers/graphTypes.ts | 14 +- .../server/test/graphql/generated/graphql.ts | 7 +- 8 files changed, 176 insertions(+), 185 deletions(-) diff --git a/packages/server/codegen.yml b/packages/server/codegen.yml index 06cf776eb2..3a977571d5 100644 --- a/packages/server/codegen.yml +++ b/packages/server/codegen.yml @@ -9,7 +9,6 @@ generates: - 'typescript-resolvers' config: contextType: '@/modules/shared/helpers/typeHelper#GraphQLContext' - allowParentTypeOverride: true mappers: Stream: '@/modules/core/helpers/graphTypes#StreamGraphQLReturn' Commit: '@/modules/core/helpers/graphTypes#CommitGraphQLReturn' @@ -107,8 +106,6 @@ config: scalars: JSONObject: Record DateTime: Date - enumValues: - WorkspaceJoinRequestStatus: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestStatusGraphQLReturn' require: - ts-node/register - tsconfig-paths/register diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 13c1b1adac..40dc3530db 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -1,4 +1,3 @@ -import { WorkspaceJoinRequestStatusGraphQLReturn as WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/helpers/graphTypes'; import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'; import { StreamGraphQLReturn, CommitGraphQLReturn, ProjectGraphQLReturn, ObjectGraphQLReturn, VersionGraphQLReturn, ServerInviteGraphQLReturnType, ModelGraphQLReturn, ModelsTreeItemGraphQLReturn, MutationsObjectGraphQLReturn, LimitedUserGraphQLReturn, UserGraphQLReturn, GraphQLEmptyReturn, StreamCollaboratorGraphQLReturn, ServerInfoGraphQLReturn, BranchGraphQLReturn } from '@/modules/core/helpers/graphTypes'; import { StreamAccessRequestGraphQLReturn, ProjectAccessRequestGraphQLReturn } from '@/modules/accessrequests/helpers/graphTypes'; @@ -25,7 +24,6 @@ export type MakeEmpty = export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; export type Omit = Pick>; export type RequireFields = Omit & { [P in K]-?: NonNullable }; -export type EnumResolverSignature = { [key in keyof T]?: AllowedValues }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: { input: string; output: string; } @@ -4426,7 +4424,11 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; -export { WorkspaceJoinRequestStatus }; +export enum WorkspaceJoinRequestStatus { + Accepted = 'accepted', + Denied = 'denied', + Pending = 'pending' +} export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; @@ -5037,7 +5039,7 @@ export type ResolversTypes = { WorkspaceInviteUseInput: WorkspaceInviteUseInput; WorkspaceJoinRequest: ResolverTypeWrapper; WorkspaceJoinRequestCollection: ResolverTypeWrapper & { items: Array }>; - WorkspaceJoinRequestStatus: WorkspaceJoinRequestStatusGraphQLReturn; + WorkspaceJoinRequestStatus: WorkspaceJoinRequestStatus; WorkspaceMutations: ResolverTypeWrapper; WorkspacePlan: ResolverTypeWrapper; WorkspacePlanStatuses: WorkspacePlanStatuses; @@ -5368,14 +5370,14 @@ export type IsOwnerDirectiveArgs = { }; export type IsOwnerDirectiveResolver = DirectiveResolverFn; -export type ActiveUserMutationsResolvers = { +export type ActiveUserMutationsResolvers = { emailMutations?: Resolver; finishOnboarding?: Resolver; update?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ActivityResolvers = { +export type ActivityResolvers = { actionType?: Resolver; id?: Resolver; info?: Resolver; @@ -5388,26 +5390,26 @@ export type ActivityResolvers; }; -export type ActivityCollectionResolvers = { +export type ActivityCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminInviteListResolvers = { +export type AdminInviteListResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminMutationsResolvers = { +export type AdminMutationsResolvers = { updateWorkspacePlan?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminQueriesResolvers = { +export type AdminQueriesResolvers = { inviteList?: Resolver>; projectList?: Resolver>; serverStatistics?: Resolver; @@ -5416,14 +5418,14 @@ export type AdminQueriesResolvers; }; -export type AdminUserListResolvers = { +export type AdminUserListResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminUserListItemResolvers = { +export type AdminUserListItemResolvers = { avatar?: Resolver, ParentType, ContextType>; company?: Resolver, ParentType, ContextType>; email?: Resolver, ParentType, ContextType>; @@ -5434,20 +5436,20 @@ export type AdminUserListItemResolvers; }; -export type AdminUsersListCollectionResolvers = { +export type AdminUsersListCollectionResolvers = { items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AdminUsersListItemResolvers = { +export type AdminUsersListItemResolvers = { id?: Resolver; invitedUser?: Resolver, ParentType, ContextType>; registeredUser?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ApiTokenResolvers = { +export type ApiTokenResolvers = { createdAt?: Resolver; id?: Resolver; lastChars?: Resolver; @@ -5458,14 +5460,14 @@ export type ApiTokenResolvers; }; -export type AppAuthorResolvers = { +export type AppAuthorResolvers = { avatar?: Resolver, ParentType, ContextType>; id?: Resolver; name?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AuthStrategyResolvers = { +export type AuthStrategyResolvers = { color?: Resolver, ParentType, ContextType>; icon?: Resolver; id?: Resolver; @@ -5474,7 +5476,7 @@ export type AuthStrategyResolvers; }; -export type AutomateFunctionResolvers = { +export type AutomateFunctionResolvers = { creator?: Resolver, ParentType, ContextType>; description?: Resolver; id?: Resolver; @@ -5489,14 +5491,14 @@ export type AutomateFunctionResolvers; }; -export type AutomateFunctionCollectionResolvers = { +export type AutomateFunctionCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateFunctionReleaseResolvers = { +export type AutomateFunctionReleaseResolvers = { commitId?: Resolver; createdAt?: Resolver; function?: Resolver; @@ -5507,14 +5509,14 @@ export type AutomateFunctionReleaseResolvers; }; -export type AutomateFunctionReleaseCollectionResolvers = { +export type AutomateFunctionReleaseCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateFunctionRunResolvers = { +export type AutomateFunctionRunResolvers = { contextView?: Resolver, ParentType, ContextType>; createdAt?: Resolver; elapsed?: Resolver; @@ -5529,7 +5531,7 @@ export type AutomateFunctionRunResolvers; }; -export type AutomateFunctionTemplateResolvers = { +export type AutomateFunctionTemplateResolvers = { id?: Resolver; logo?: Resolver; title?: Resolver; @@ -5537,20 +5539,20 @@ export type AutomateFunctionTemplateResolvers; }; -export type AutomateFunctionTokenResolvers = { +export type AutomateFunctionTokenResolvers = { functionId?: Resolver; functionToken?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateMutationsResolvers = { +export type AutomateMutationsResolvers = { createFunction?: Resolver>; createFunctionWithoutVersion?: Resolver>; updateFunction?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomateRunResolvers = { +export type AutomateRunResolvers = { automation?: Resolver; automationId?: Resolver; createdAt?: Resolver; @@ -5562,14 +5564,14 @@ export type AutomateRunResolvers; }; -export type AutomateRunCollectionResolvers = { +export type AutomateRunCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationResolvers = { +export type AutomationResolvers = { createdAt?: Resolver; creationPublicKeys?: Resolver, ParentType, ContextType>; currentRevision?: Resolver, ParentType, ContextType>; @@ -5582,35 +5584,35 @@ export type AutomationResolvers; }; -export type AutomationCollectionResolvers = { +export type AutomationCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationRevisionResolvers = { +export type AutomationRevisionResolvers = { functions?: Resolver, ParentType, ContextType>; id?: Resolver; triggerDefinitions?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationRevisionFunctionResolvers = { +export type AutomationRevisionFunctionResolvers = { parameters?: Resolver, ParentType, ContextType>; release?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type AutomationRevisionTriggerDefinitionResolvers = { +export type AutomationRevisionTriggerDefinitionResolvers = { __resolveType: TypeResolveFn<'VersionCreatedTriggerDefinition', ParentType, ContextType>; }; -export type AutomationRunTriggerResolvers = { +export type AutomationRunTriggerResolvers = { __resolveType: TypeResolveFn<'VersionCreatedTrigger', ParentType, ContextType>; }; -export type BasicGitRepositoryMetadataResolvers = { +export type BasicGitRepositoryMetadataResolvers = { id?: Resolver; name?: Resolver; owner?: Resolver; @@ -5622,7 +5624,7 @@ export interface BigIntScalarConfig extends GraphQLScalarTypeConfig = { +export type BlobMetadataResolvers = { createdAt?: Resolver; fileHash?: Resolver, ParentType, ContextType>; fileName?: Resolver; @@ -5636,7 +5638,7 @@ export type BlobMetadataResolvers; }; -export type BlobMetadataCollectionResolvers = { +export type BlobMetadataCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; @@ -5644,7 +5646,7 @@ export type BlobMetadataCollectionResolvers; }; -export type BranchResolvers = { +export type BranchResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; author?: Resolver, ParentType, ContextType>; commits?: Resolver, ParentType, ContextType, RequireFields>; @@ -5655,14 +5657,14 @@ export type BranchResolvers; }; -export type BranchCollectionResolvers = { +export type BranchCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CheckoutSessionResolvers = { +export type CheckoutSessionResolvers = { billingInterval?: Resolver; createdAt?: Resolver; id?: Resolver; @@ -5673,7 +5675,7 @@ export type CheckoutSessionResolvers; }; -export type CommentResolvers = { +export type CommentResolvers = { archived?: Resolver; author?: Resolver; authorId?: Resolver; @@ -5696,20 +5698,20 @@ export type CommentResolvers; }; -export type CommentActivityMessageResolvers = { +export type CommentActivityMessageResolvers = { comment?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommentCollectionResolvers = { +export type CommentCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommentDataFiltersResolvers = { +export type CommentDataFiltersResolvers = { hiddenIds?: Resolver>, ParentType, ContextType>; isolatedIds?: Resolver>, ParentType, ContextType>; passMax?: Resolver, ParentType, ContextType>; @@ -5719,7 +5721,7 @@ export type CommentDataFiltersResolvers; }; -export type CommentMutationsResolvers = { +export type CommentMutationsResolvers = { archive?: Resolver>; create?: Resolver>; edit?: Resolver>; @@ -5728,20 +5730,20 @@ export type CommentMutationsResolvers; }; -export type CommentReplyAuthorCollectionResolvers = { +export type CommentReplyAuthorCollectionResolvers = { items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommentThreadActivityMessageResolvers = { +export type CommentThreadActivityMessageResolvers = { data?: Resolver, ParentType, ContextType>; reply?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CommitResolvers = { +export type CommitResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; authorAvatar?: Resolver, ParentType, ContextType>; authorId?: Resolver, ParentType, ContextType>; @@ -5762,14 +5764,14 @@ export type CommitResolvers; }; -export type CommitCollectionResolvers = { +export type CommitCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type CountOnlyCollectionResolvers = { +export type CountOnlyCollectionResolvers = { totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; @@ -5778,7 +5780,7 @@ export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig = { +export type FileUploadResolvers = { branchName?: Resolver; convertedCommitId?: Resolver, ParentType, ContextType>; convertedLastUpdate?: Resolver; @@ -5799,7 +5801,7 @@ export type FileUploadResolvers; }; -export type GendoAiRenderResolvers = { +export type GendoAiRenderResolvers = { camera?: Resolver, ParentType, ContextType>; createdAt?: Resolver; gendoGenerationId?: Resolver, ParentType, ContextType>; @@ -5816,7 +5818,7 @@ export type GendoAiRenderResolvers; }; -export type GendoAiRenderCollectionResolvers = { +export type GendoAiRenderCollectionResolvers = { items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; @@ -5826,7 +5828,7 @@ export interface JsonObjectScalarConfig extends GraphQLScalarTypeConfig = { +export type LegacyCommentViewerDataResolvers = { camPos?: Resolver, ParentType, ContextType>; filters?: Resolver; location?: Resolver; @@ -5835,7 +5837,7 @@ export type LegacyCommentViewerDataResolvers; }; -export type LimitedUserResolvers = { +export type LimitedUserResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; avatar?: Resolver, ParentType, ContextType>; bio?: Resolver, ParentType, ContextType>; @@ -5853,7 +5855,7 @@ export type LimitedUserResolvers; }; -export type LimitedWorkspaceResolvers = { +export type LimitedWorkspaceResolvers = { defaultLogoIndex?: Resolver; description?: Resolver, ParentType, ContextType>; id?: Resolver; @@ -5863,7 +5865,7 @@ export type LimitedWorkspaceResolvers; }; -export type ModelResolvers = { +export type ModelResolvers = { author?: Resolver; automationsStatus?: Resolver, ParentType, ContextType>; childrenTree?: Resolver, ParentType, ContextType>; @@ -5881,21 +5883,21 @@ export type ModelResolvers; }; -export type ModelCollectionResolvers = { +export type ModelCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ModelMutationsResolvers = { +export type ModelMutationsResolvers = { create?: Resolver>; delete?: Resolver>; update?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ModelsTreeItemResolvers = { +export type ModelsTreeItemResolvers = { children?: Resolver, ParentType, ContextType>; fullName?: Resolver; hasChildren?: Resolver; @@ -5906,14 +5908,14 @@ export type ModelsTreeItemResolvers; }; -export type ModelsTreeItemCollectionResolvers = { +export type ModelsTreeItemCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type MutationResolvers = { +export type MutationResolvers = { _?: Resolver, ParentType, ContextType>; activeUserMutations?: Resolver; admin?: Resolver; @@ -5981,7 +5983,7 @@ export type MutationResolvers; }; -export type ObjectResolvers = { +export type ObjectResolvers = { applicationId?: Resolver, ParentType, ContextType>; children?: Resolver>; commentCount?: Resolver; @@ -5993,26 +5995,26 @@ export type ObjectResolvers; }; -export type ObjectCollectionResolvers = { +export type ObjectCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; objects?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type PasswordStrengthCheckFeedbackResolvers = { +export type PasswordStrengthCheckFeedbackResolvers = { suggestions?: Resolver, ParentType, ContextType>; warning?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type PasswordStrengthCheckResultsResolvers = { +export type PasswordStrengthCheckResultsResolvers = { feedback?: Resolver; score?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type PendingStreamCollaboratorResolvers = { +export type PendingStreamCollaboratorResolvers = { id?: Resolver; inviteId?: Resolver; invitedBy?: Resolver; @@ -6027,7 +6029,7 @@ export type PendingStreamCollaboratorResolvers; }; -export type PendingWorkspaceCollaboratorResolvers = { +export type PendingWorkspaceCollaboratorResolvers = { email?: Resolver, ParentType, ContextType>; id?: Resolver; inviteId?: Resolver; @@ -6043,7 +6045,7 @@ export type PendingWorkspaceCollaboratorResolvers; }; -export type ProjectResolvers = { +export type ProjectResolvers = { allowPublicComments?: Resolver; automation?: Resolver>; automations?: Resolver>; @@ -6078,7 +6080,7 @@ export type ProjectResolvers; }; -export type ProjectAccessRequestResolvers = { +export type ProjectAccessRequestResolvers = { createdAt?: Resolver; id?: Resolver; project?: Resolver; @@ -6088,13 +6090,13 @@ export type ProjectAccessRequestResolvers; }; -export type ProjectAccessRequestMutationsResolvers = { +export type ProjectAccessRequestMutationsResolvers = { create?: Resolver>; use?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectAutomationMutationsResolvers = { +export type ProjectAutomationMutationsResolvers = { create?: Resolver>; createRevision?: Resolver>; createTestAutomation?: Resolver>; @@ -6104,7 +6106,7 @@ export type ProjectAutomationMutationsResolvers; }; -export type ProjectAutomationsUpdatedMessageResolvers = { +export type ProjectAutomationsUpdatedMessageResolvers = { automation?: Resolver, ParentType, ContextType>; automationId?: Resolver; revision?: Resolver, ParentType, ContextType>; @@ -6112,21 +6114,21 @@ export type ProjectAutomationsUpdatedMessageResolvers; }; -export type ProjectCollaboratorResolvers = { +export type ProjectCollaboratorResolvers = { id?: Resolver; role?: Resolver; user?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectCollectionResolvers = { +export type ProjectCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectCommentCollectionResolvers = { +export type ProjectCommentCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalArchivedCount?: Resolver; @@ -6134,21 +6136,21 @@ export type ProjectCommentCollectionResolvers; }; -export type ProjectCommentsUpdatedMessageResolvers = { +export type ProjectCommentsUpdatedMessageResolvers = { comment?: Resolver, ParentType, ContextType>; id?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectFileImportUpdatedMessageResolvers = { +export type ProjectFileImportUpdatedMessageResolvers = { id?: Resolver; type?: Resolver; upload?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectInviteMutationsResolvers = { +export type ProjectInviteMutationsResolvers = { batchCreate?: Resolver>; cancel?: Resolver>; create?: Resolver>; @@ -6157,14 +6159,14 @@ export type ProjectInviteMutationsResolvers; }; -export type ProjectModelsUpdatedMessageResolvers = { +export type ProjectModelsUpdatedMessageResolvers = { id?: Resolver; model?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectMutationsResolvers = { +export type ProjectMutationsResolvers = { accessRequestMutations?: Resolver; automationMutations?: Resolver>; batchDelete?: Resolver>; @@ -6178,27 +6180,27 @@ export type ProjectMutationsResolvers; }; -export type ProjectPendingModelsUpdatedMessageResolvers = { +export type ProjectPendingModelsUpdatedMessageResolvers = { id?: Resolver; model?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectPendingVersionsUpdatedMessageResolvers = { +export type ProjectPendingVersionsUpdatedMessageResolvers = { id?: Resolver; type?: Resolver; version?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectRoleResolvers = { +export type ProjectRoleResolvers = { project?: Resolver; role?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectTriggeredAutomationsStatusUpdatedMessageResolvers = { +export type ProjectTriggeredAutomationsStatusUpdatedMessageResolvers = { model?: Resolver; project?: Resolver; run?: Resolver; @@ -6207,21 +6209,21 @@ export type ProjectTriggeredAutomationsStatusUpdatedMessageResolvers; }; -export type ProjectUpdatedMessageResolvers = { +export type ProjectUpdatedMessageResolvers = { id?: Resolver; project?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectVersionsPreviewGeneratedMessageResolvers = { +export type ProjectVersionsPreviewGeneratedMessageResolvers = { objectId?: Resolver; projectId?: Resolver; versionId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ProjectVersionsUpdatedMessageResolvers = { +export type ProjectVersionsUpdatedMessageResolvers = { id?: Resolver; modelId?: Resolver; type?: Resolver; @@ -6229,7 +6231,7 @@ export type ProjectVersionsUpdatedMessageResolvers; }; -export type QueryResolvers = { +export type QueryResolvers = { _?: Resolver, ParentType, ContextType>; activeUser?: Resolver, ParentType, ContextType>; admin?: Resolver; @@ -6268,26 +6270,26 @@ export type QueryResolvers, ParentType, ContextType, RequireFields>; }; -export type ResourceIdentifierResolvers = { +export type ResourceIdentifierResolvers = { resourceId?: Resolver; resourceType?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type RoleResolvers = { +export type RoleResolvers = { description?: Resolver; name?: Resolver; resourceTarget?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ScopeResolvers = { +export type ScopeResolvers = { description?: Resolver; name?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerAppResolvers = { +export type ServerAppResolvers = { author?: Resolver, ParentType, ContextType>; createdAt?: Resolver; description?: Resolver, ParentType, ContextType>; @@ -6303,7 +6305,7 @@ export type ServerAppResolvers; }; -export type ServerAppListItemResolvers = { +export type ServerAppListItemResolvers = { author?: Resolver, ParentType, ContextType>; description?: Resolver, ParentType, ContextType>; id?: Resolver; @@ -6315,19 +6317,19 @@ export type ServerAppListItemResolvers; }; -export type ServerAutomateInfoResolvers = { +export type ServerAutomateInfoResolvers = { availableFunctionTemplates?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerConfigurationResolvers = { +export type ServerConfigurationResolvers = { blobSizeLimitBytes?: Resolver; objectMultipartUploadSizeLimitBytes?: Resolver; objectSizeLimitBytes?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerInfoResolvers = { +export type ServerInfoResolvers = { adminContact?: Resolver, ParentType, ContextType>; authStrategies?: Resolver, ParentType, ContextType>; automate?: Resolver; @@ -6352,31 +6354,31 @@ export type ServerInfoResolvers; }; -export type ServerInfoMutationsResolvers = { +export type ServerInfoMutationsResolvers = { multiRegion?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerInviteResolvers = { +export type ServerInviteResolvers = { email?: Resolver; id?: Resolver; invitedBy?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerMigrationResolvers = { +export type ServerMigrationResolvers = { movedFrom?: Resolver, ParentType, ContextType>; movedTo?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerMultiRegionConfigurationResolvers = { +export type ServerMultiRegionConfigurationResolvers = { availableKeys?: Resolver, ParentType, ContextType>; regions?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerRegionItemResolvers = { +export type ServerRegionItemResolvers = { description?: Resolver, ParentType, ContextType>; id?: Resolver; key?: Resolver; @@ -6384,26 +6386,26 @@ export type ServerRegionItemResolvers; }; -export type ServerRegionMutationsResolvers = { +export type ServerRegionMutationsResolvers = { create?: Resolver>; update?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerRoleItemResolvers = { +export type ServerRoleItemResolvers = { id?: Resolver; title?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerStatisticsResolvers = { +export type ServerStatisticsResolvers = { totalPendingInvites?: Resolver; totalProjectCount?: Resolver; totalUserCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type ServerStatsResolvers = { +export type ServerStatsResolvers = { commitHistory?: Resolver>>, ParentType, ContextType>; objectHistory?: Resolver>>, ParentType, ContextType>; streamHistory?: Resolver>>, ParentType, ContextType>; @@ -6415,12 +6417,12 @@ export type ServerStatsResolvers; }; -export type ServerWorkspacesInfoResolvers = { +export type ServerWorkspacesInfoResolvers = { workspacesEnabled?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type SmartTextEditorValueResolvers = { +export type SmartTextEditorValueResolvers = { attachments?: Resolver>, ParentType, ContextType>; doc?: Resolver, ParentType, ContextType>; type?: Resolver; @@ -6428,7 +6430,7 @@ export type SmartTextEditorValueResolvers; }; -export type StreamResolvers = { +export type StreamResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; allowPublicComments?: Resolver; blob?: Resolver, ParentType, ContextType, RequireFields>; @@ -6459,7 +6461,7 @@ export type StreamResolvers; }; -export type StreamAccessRequestResolvers = { +export type StreamAccessRequestResolvers = { createdAt?: Resolver; id?: Resolver; requester?: Resolver; @@ -6469,7 +6471,7 @@ export type StreamAccessRequestResolvers; }; -export type StreamCollaboratorResolvers = { +export type StreamCollaboratorResolvers = { avatar?: Resolver, ParentType, ContextType>; company?: Resolver, ParentType, ContextType>; id?: Resolver; @@ -6479,14 +6481,14 @@ export type StreamCollaboratorResolvers; }; -export type StreamCollectionResolvers = { +export type StreamCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type SubscriptionResolvers = { +export type SubscriptionResolvers = { _?: SubscriptionResolver, "_", ParentType, ContextType>; branchCreated?: SubscriptionResolver, "branchCreated", ParentType, ContextType, RequireFields>; branchDeleted?: SubscriptionResolver, "branchDeleted", ParentType, ContextType, RequireFields>; @@ -6520,32 +6522,32 @@ export type SubscriptionResolvers>; }; -export type TestAutomationRunResolvers = { +export type TestAutomationRunResolvers = { automationRunId?: Resolver; functionRunId?: Resolver; triggers?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type TestAutomationRunTriggerResolvers = { +export type TestAutomationRunTriggerResolvers = { payload?: Resolver; triggerType?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type TestAutomationRunTriggerPayloadResolvers = { +export type TestAutomationRunTriggerPayloadResolvers = { modelId?: Resolver; versionId?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type TokenResourceIdentifierResolvers = { +export type TokenResourceIdentifierResolvers = { id?: Resolver; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type TriggeredAutomationsStatusResolvers = { +export type TriggeredAutomationsStatusResolvers = { automationRuns?: Resolver, ParentType, ContextType>; id?: Resolver; status?: Resolver; @@ -6553,7 +6555,7 @@ export type TriggeredAutomationsStatusResolvers; }; -export type UserResolvers = { +export type UserResolvers = { activity?: Resolver, ParentType, ContextType, RequireFields>; apiTokens?: Resolver, ParentType, ContextType>; authorizedApps?: Resolver>, ParentType, ContextType>; @@ -6591,13 +6593,13 @@ export type UserResolvers; }; -export type UserAutomateInfoResolvers = { +export type UserAutomateInfoResolvers = { availableGithubOrgs?: Resolver, ParentType, ContextType>; hasAutomateGithubApp?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserEmailResolvers = { +export type UserEmailResolvers = { email?: Resolver; id?: Resolver; primary?: Resolver; @@ -6606,7 +6608,7 @@ export type UserEmailResolvers; }; -export type UserEmailMutationsResolvers = { +export type UserEmailMutationsResolvers = { create?: Resolver>; delete?: Resolver>; requestNewEmailVerification?: Resolver, ParentType, ContextType, RequireFields>; @@ -6614,14 +6616,14 @@ export type UserEmailMutationsResolvers; }; -export type UserGendoAiCreditsResolvers = { +export type UserGendoAiCreditsResolvers = { limit?: Resolver; resetDate?: Resolver; used?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserProjectCollectionResolvers = { +export type UserProjectCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; numberOfHidden?: Resolver; @@ -6629,20 +6631,20 @@ export type UserProjectCollectionResolvers; }; -export type UserProjectsUpdatedMessageResolvers = { +export type UserProjectsUpdatedMessageResolvers = { id?: Resolver; project?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserSearchResultCollectionResolvers = { +export type UserSearchResultCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type UserStreamCollectionResolvers = { +export type UserStreamCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver>, ParentType, ContextType>; numberOfHidden?: Resolver; @@ -6650,7 +6652,7 @@ export type UserStreamCollectionResolvers; }; -export type VersionResolvers = { +export type VersionResolvers = { authorUser?: Resolver, ParentType, ContextType>; automationsStatus?: Resolver, ParentType, ContextType>; commentThreads?: Resolver>; @@ -6668,27 +6670,27 @@ export type VersionResolvers; }; -export type VersionCollectionResolvers = { +export type VersionCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type VersionCreatedTriggerResolvers = { +export type VersionCreatedTriggerResolvers = { model?: Resolver, ParentType, ContextType>; type?: Resolver; version?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type VersionCreatedTriggerDefinitionResolvers = { +export type VersionCreatedTriggerDefinitionResolvers = { model?: Resolver, ParentType, ContextType>; type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type VersionMutationsResolvers = { +export type VersionMutationsResolvers = { create?: Resolver>; delete?: Resolver>; markReceived?: Resolver>; @@ -6698,20 +6700,20 @@ export type VersionMutationsResolvers; }; -export type ViewerResourceGroupResolvers = { +export type ViewerResourceGroupResolvers = { identifier?: Resolver; items?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ViewerResourceItemResolvers = { +export type ViewerResourceItemResolvers = { modelId?: Resolver, ParentType, ContextType>; objectId?: Resolver; versionId?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type ViewerUserActivityMessageResolvers = { +export type ViewerUserActivityMessageResolvers = { sessionId?: Resolver; state?: Resolver, ParentType, ContextType>; status?: Resolver; @@ -6721,7 +6723,7 @@ export type ViewerUserActivityMessageResolvers; }; -export type WebhookResolvers = { +export type WebhookResolvers = { description?: Resolver, ParentType, ContextType>; enabled?: Resolver, ParentType, ContextType>; hasSecret?: Resolver; @@ -6734,13 +6736,13 @@ export type WebhookResolvers; }; -export type WebhookCollectionResolvers = { +export type WebhookCollectionResolvers = { items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WebhookEventResolvers = { +export type WebhookEventResolvers = { id?: Resolver; lastUpdate?: Resolver; payload?: Resolver; @@ -6751,13 +6753,13 @@ export type WebhookEventResolvers; }; -export type WebhookEventCollectionResolvers = { +export type WebhookEventCollectionResolvers = { items?: Resolver>>, ParentType, ContextType>; totalCount?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceResolvers = { +export type WorkspaceResolvers = { adminWorkspacesJoinRequests?: Resolver>; automateFunctions?: Resolver>; createdAt?: Resolver; @@ -6787,14 +6789,14 @@ export type WorkspaceResolvers; }; -export type WorkspaceBillingMutationsResolvers = { +export type WorkspaceBillingMutationsResolvers = { cancelCheckoutSession?: Resolver>; createCheckoutSession?: Resolver>; upgradePlan?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceCollaboratorResolvers = { +export type WorkspaceCollaboratorResolvers = { id?: Resolver; projectRoles?: Resolver, ParentType, ContextType>; role?: Resolver; @@ -6802,33 +6804,33 @@ export type WorkspaceCollaboratorResolvers; }; -export type WorkspaceCollaboratorCollectionResolvers = { +export type WorkspaceCollaboratorCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceCollectionResolvers = { +export type WorkspaceCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceCreationStateResolvers = { +export type WorkspaceCreationStateResolvers = { completed?: Resolver; state?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceDomainResolvers = { +export type WorkspaceDomainResolvers = { domain?: Resolver; id?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceInviteMutationsResolvers = { +export type WorkspaceInviteMutationsResolvers = { batchCreate?: Resolver>; cancel?: Resolver>; create?: Resolver>; @@ -6837,7 +6839,7 @@ export type WorkspaceInviteMutationsResolvers; }; -export type WorkspaceJoinRequestResolvers = { +export type WorkspaceJoinRequestResolvers = { createdAt?: Resolver; status?: Resolver; user?: Resolver; @@ -6845,16 +6847,14 @@ export type WorkspaceJoinRequestResolvers; }; -export type WorkspaceJoinRequestCollectionResolvers = { +export type WorkspaceJoinRequestCollectionResolvers = { cursor?: Resolver, ParentType, ContextType>; items?: Resolver, ParentType, ContextType>; totalCount?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceJoinRequestStatusResolvers = EnumResolverSignature<{ accepted?: any, denied?: any, pending?: any }, ResolversTypes['WorkspaceJoinRequestStatus']>; - -export type WorkspaceMutationsResolvers = { +export type WorkspaceMutationsResolvers = { addDomain?: Resolver>; billing?: Resolver; create?: Resolver>; @@ -6874,21 +6874,21 @@ export type WorkspaceMutationsResolvers; }; -export type WorkspacePlanResolvers = { +export type WorkspacePlanResolvers = { createdAt?: Resolver; name?: Resolver; status?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceProjectMutationsResolvers = { +export type WorkspaceProjectMutationsResolvers = { create?: Resolver>; moveToWorkspace?: Resolver>; updateRole?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceProjectsUpdatedMessageResolvers = { +export type WorkspaceProjectsUpdatedMessageResolvers = { project?: Resolver, ParentType, ContextType>; projectId?: Resolver; type?: Resolver; @@ -6896,13 +6896,13 @@ export type WorkspaceProjectsUpdatedMessageResolvers; }; -export type WorkspaceSsoResolvers = { +export type WorkspaceSsoResolvers = { provider?: Resolver, ParentType, ContextType>; session?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceSsoProviderResolvers = { +export type WorkspaceSsoProviderResolvers = { clientId?: Resolver; id?: Resolver; issuerUrl?: Resolver; @@ -6910,13 +6910,13 @@ export type WorkspaceSsoProviderResolvers; }; -export type WorkspaceSsoSessionResolvers = { +export type WorkspaceSsoSessionResolvers = { createdAt?: Resolver; validUntil?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceSubscriptionResolvers = { +export type WorkspaceSubscriptionResolvers = { billingInterval?: Resolver; createdAt?: Resolver; currentBillingCycleEnd?: Resolver; @@ -6925,13 +6925,13 @@ export type WorkspaceSubscriptionResolvers; }; -export type WorkspaceSubscriptionSeatsResolvers = { +export type WorkspaceSubscriptionSeatsResolvers = { guest?: Resolver; plan?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type WorkspaceUpdatedMessageResolvers = { +export type WorkspaceUpdatedMessageResolvers = { id?: Resolver; workspace?: Resolver; __isTypeOf?: IsTypeOfResolverFn; @@ -7085,7 +7085,6 @@ export type Resolvers = { WorkspaceInviteMutations?: WorkspaceInviteMutationsResolvers; WorkspaceJoinRequest?: WorkspaceJoinRequestResolvers; WorkspaceJoinRequestCollection?: WorkspaceJoinRequestCollectionResolvers; - WorkspaceJoinRequestStatus?: WorkspaceJoinRequestStatusResolvers; WorkspaceMutations?: WorkspaceMutationsResolvers; WorkspacePlan?: WorkspacePlanResolvers; WorkspaceProjectMutations?: WorkspaceProjectMutationsResolvers; 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 d724c6e81c..b907c0cc74 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -1,4 +1,3 @@ -import { WorkspaceJoinRequestStatusGraphQLReturn as WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/helpers/graphTypes'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -4406,7 +4405,11 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; -export { WorkspaceJoinRequestStatus }; +export enum WorkspaceJoinRequestStatus { + Accepted = 'accepted', + Denied = 'denied', + Pending = 'pending' +} export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts index b1654e4dac..e7c66549b3 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts @@ -18,6 +18,7 @@ export default { limit: number cursor?: string filter: { + workspaceId: string userId: string status?: WorkspaceJoinRequestStatus | null } diff --git a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts index 169abbd7ef..13a77feed3 100644 --- a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts @@ -74,7 +74,7 @@ export const getAdminWorkspaceJoinRequestsFactory = query.andWhere(WorkspaceJoinRequests.col.createdAt, '<', cursor) } return await query - .select(WorkspaceJoinRequests.cols) + .select(WorkspaceJoinRequests.cols) .orderBy(WorkspaceJoinRequests.col.createdAt, 'desc') .limit(limit) } diff --git a/packages/server/modules/workspacesCore/domain/types.ts b/packages/server/modules/workspacesCore/domain/types.ts index 65d4ff5f91..182d60cfa7 100644 --- a/packages/server/modules/workspacesCore/domain/types.ts +++ b/packages/server/modules/workspacesCore/domain/types.ts @@ -67,11 +67,7 @@ export type WorkspaceRegionAssignment = { createdAt: Date } -export type WorkspaceJoinRequestStatus = - | 'pending' - | 'accepted' - | 'rejected' - | 'dismissed' +export type WorkspaceJoinRequestStatus = 'pending' | 'accepted' | 'denied' | 'dismissed' export type WorkspaceJoinRequest = { workspaceId: string diff --git a/packages/server/modules/workspacesCore/helpers/graphTypes.ts b/packages/server/modules/workspacesCore/helpers/graphTypes.ts index 0fab0f1644..427a315915 100644 --- a/packages/server/modules/workspacesCore/helpers/graphTypes.ts +++ b/packages/server/modules/workspacesCore/helpers/graphTypes.ts @@ -1,20 +1,12 @@ import { MutationsObjectGraphQLReturn } from '@/modules/core/helpers/graphTypes' -import { LimitedUserRecord, UserRecord } from '@/modules/core/helpers/types' +import { LimitedUserRecord } from '@/modules/core/helpers/types' import { WorkspaceSsoProviderRecord } from '@/modules/workspaces/domain/sso/types' import { WorkspaceTeamMember } from '@/modules/workspaces/domain/types' -import { - Workspace, - WorkspaceJoinRequest, - WorkspaceJoinRequestStatus -} from '@/modules/workspacesCore/domain/types' +import { Workspace, WorkspaceJoinRequest } from '@/modules/workspacesCore/domain/types' import { WorkspaceRoles } from '@speckle/shared' export type WorkspaceGraphQLReturn = Workspace -export type WorkspaceJoinRequestGraphQLReturn = WorkspaceJoinRequest & { - user: UserRecord - workspace: Workspace -} -export type WorkspaceJoinRequestStatusGraphQLReturn = WorkspaceJoinRequestStatus +export type WorkspaceJoinRequestGraphQLReturn = WorkspaceJoinRequest export type WorkspaceBillingGraphQLReturn = { parent: Workspace } export type WorkspaceSsoGraphQLReturn = WorkspaceSsoProviderRecord export type WorkspaceMutationsGraphQLReturn = MutationsObjectGraphQLReturn diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 762ccf56c2..d64d8de797 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -1,4 +1,3 @@ -import { WorkspaceJoinRequestStatusGraphQLReturn as WorkspaceJoinRequestStatus } from '@/modules/workspacesCore/helpers/graphTypes'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; export type Maybe = T | null; export type InputMaybe = Maybe; @@ -4407,7 +4406,11 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; -export { WorkspaceJoinRequestStatus }; +export enum WorkspaceJoinRequestStatus { + Accepted = 'accepted', + Denied = 'denied', + Pending = 'pending' +} export type WorkspaceMutations = { __typename?: 'WorkspaceMutations'; From d7b6d6b27e777dbe3bcece9366b94eedd427060c Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Tue, 14 Jan 2025 18:14:09 +0100 Subject: [PATCH 09/10] chore(workspaces): remove isOwner directive --- .../server/assets/workspacesCore/typedefs/workspaces.graphql | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index d1c85e13a8..ab9797c64d 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -322,7 +322,6 @@ type Workspace { @hasServerRole(role: SERVER_USER) @hasScope(scope: "workspace:read") @hasWorkspaceRole(role: ADMIN) - @isOwner } type WorkspaceCreationState { From 507c83ef80fe7680571290bf9f84f5f446514230 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Wed, 15 Jan 2025 15:42:21 +0100 Subject: [PATCH 10/10] chore(workspaces): fix test types --- .../workspaceJoinRequests.graph.spec.ts | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts index a95db72052..ec10b513b8 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts @@ -13,7 +13,7 @@ import { import { createTestContext, testApolloServer } from '@/test/graphqlHelper' import { beforeEachContext } from '@/test/hooks' import { AllScopes, Roles } from '@speckle/shared' -import { assert, expect } from 'chai' +import { expect } from 'chai' import { upsertWorkspaceRoleFactory } from '@/modules/workspaces/repositories/workspaces' async function login(user: BasicTestUser) { @@ -115,12 +115,9 @@ describe('WorkspaceJoinRequests GQL', () => { expect(totalCount1).to.equal(1) expect(items1).to.have.length(1) - assert.deepEqual(items1[0], { - status: 'pending', - user: { id: user1.id, name: user1.name }, - workspace: { id: workspace1.id, name: workspace1.name }, - createdAt: items1[0].createdAt - }) + expect(items1[0].status).to.equal('pending') + expect(items1[0].workspace.id).to.equal(workspace1.id) + expect(items1[0].user.id).to.equal(user1.id) const workspace2Res = await sessionAdmin.execute( GetWorkspaceWithJoinRequestsDocument, @@ -136,12 +133,9 @@ describe('WorkspaceJoinRequests GQL', () => { expect(totalCount2).to.equal(1) expect(items2).to.have.length(1) - assert.deepEqual(items2[0], { - status: 'pending', - user: { id: user2.id, name: user2.name }, - workspace: { id: workspace2.id, name: workspace2.name }, - createdAt: items2[0].createdAt - }) + expect(items2[0].status).to.equal('pending') + expect(items2[0].workspace.id).to.equal(workspace2.id) + expect(items2[0].user.id).to.equal(user2.id) }) }) })