From 00c01db92345016fe36f87bbf44efbfc8ac3cad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= <57442769+gjedlicska@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:29:36 +0200 Subject: [PATCH] feat(workspaces): add workspace slug support (#2982) * feat(workspaces): add workspace slug support * chore(workspaces): lint * feat(workspaces): add slug validation and generation * fix(workspaces): test lint miss --- .../typedefs/workspaces.graphql | 7 + .../modules/core/graph/generated/graphql.ts | 13 +- .../graph/generated/graphql.ts | 11 +- .../modules/shared/test/unit/eventBus.spec.ts | 1 + .../modules/workspaces/domain/operations.ts | 10 ++ .../modules/workspaces/errors/workspace.ts | 12 ++ .../workspaces/graph/resolvers/workspaces.ts | 29 +++- .../server/modules/workspaces/helpers/db.ts | 1 + .../workspaces/repositories/workspaces.ts | 69 +++++--- .../modules/workspaces/services/management.ts | 68 +++++++- .../workspaces/tests/helpers/creation.ts | 24 ++- .../workspaces/tests/helpers/graphql.ts | 1 + .../tests/integration/invites.graph.spec.ts | 9 + .../tests/integration/projects.graph.spec.ts | 2 + .../tests/integration/repositories.spec.ts | 69 +++++++- .../tests/integration/roles.graph.spec.ts | 4 + .../integration/workspaces.graph.spec.ts | 23 ++- .../tests/unit/services/domains.spec.ts | 2 + .../tests/unit/services/join.spec.ts | 1 + .../tests/unit/services/management.spec.ts | 159 +++++++++++++++++- .../modules/workspacesCore/domain/types.ts | 1 + .../20240912134548_add_workspace_slug.ts | 17 ++ .../server/test/graphql/generated/graphql.ts | 59 ++++--- packages/server/test/graphql/workspaces.ts | 1 + .../shared/src/workspaces/errors/index.ts | 7 + 25 files changed, 518 insertions(+), 82 deletions(-) create mode 100644 packages/server/modules/workspacesCore/migrations/20240912134548_add_workspace_slug.ts diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 66d42288c..4b1c8affb 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -11,10 +11,15 @@ extend type Query { Either token or workspaceId must be specified, or both """ workspaceInvite(workspaceId: String, token: String): PendingWorkspaceCollaborator + """ + Validates the slug, to make sure it contains only valid characters and its not taken. + """ + validateWorkspaceSlug(slug: String!): Boolean! } input WorkspaceCreateInput { name: String! + slug: String description: String defaultLogoIndex: Int """ @@ -26,6 +31,7 @@ input WorkspaceCreateInput { input WorkspaceUpdateInput { id: String! name: String + slug: String description: String """ Logo image as base64-encoded string @@ -248,6 +254,7 @@ type WorkspaceBilling { type Workspace { id: ID! name: String! + slug: String! description: String createdAt: DateTime! updatedAt: DateTime! diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 18b9449fb..31fd42754 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -2505,6 +2505,8 @@ export type Query = { * The query looks for matches in name & email */ userSearch: UserSearchResultCollection; + /** Validates the slug, to make sure it contains only valid characters and its not taken. */ + validateWorkspaceSlug: Scalars['Boolean']['output']; workspace: Workspace; /** * Look for an invitation to a workspace, for the current user (authed or not). @@ -2640,6 +2642,11 @@ export type QueryUserSearchArgs = { }; +export type QueryValidateWorkspaceSlugArgs = { + slug: Scalars['String']['input']; +}; + + export type QueryWorkspaceArgs = { id: Scalars['String']['input']; }; @@ -3902,6 +3909,7 @@ export type Workspace = { projects: ProjectCollection; /** Active user's role for this workspace. `null` if request is not authenticated, or the workspace is not explicitly shared with you. */ role?: Maybe; + slug: Scalars['String']['output']; team: WorkspaceCollaboratorCollection; updatedAt: Scalars['DateTime']['output']; }; @@ -3987,6 +3995,7 @@ export type WorkspaceCreateInput = { /** Logo image as base64-encoded string */ logo?: InputMaybe; name: Scalars['String']['input']; + slug?: InputMaybe; }; export type WorkspaceDomain = { @@ -4179,7 +4188,6 @@ export type WorkspaceTeamFilter = { export type WorkspaceUpdateInput = { defaultLogoIndex?: InputMaybe; - /** stream:reviewer | stream:contributor */ defaultProjectRole?: InputMaybe; description?: InputMaybe; discoverabilityEnabled?: InputMaybe; @@ -4188,6 +4196,7 @@ export type WorkspaceUpdateInput = { /** Logo image as base64-encoded string */ logo?: InputMaybe; name?: InputMaybe; + slug?: InputMaybe; }; export type WorkspaceVersionsCount = { @@ -5671,6 +5680,7 @@ export type QueryResolvers, ParentType, ContextType, Partial>; userPwdStrength?: Resolver>; userSearch?: Resolver>; + validateWorkspaceSlug?: Resolver>; workspace?: Resolver>; workspaceInvite?: Resolver, ParentType, ContextType, Partial>; }; @@ -6124,6 +6134,7 @@ export type WorkspaceResolvers; projects?: Resolver>; role?: Resolver, ParentType, ContextType>; + slug?: Resolver; team?: Resolver>; updatedAt?: Resolver; __isTypeOf?: IsTypeOfResolverFn; 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 a6f77deb6..e67ff242c 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -2491,6 +2491,8 @@ export type Query = { * The query looks for matches in name & email */ userSearch: UserSearchResultCollection; + /** Validates the slug, to make sure it contains only valid characters and its not taken. */ + validateWorkspaceSlug: Scalars['Boolean']['output']; workspace: Workspace; /** * Look for an invitation to a workspace, for the current user (authed or not). @@ -2626,6 +2628,11 @@ export type QueryUserSearchArgs = { }; +export type QueryValidateWorkspaceSlugArgs = { + slug: Scalars['String']['input']; +}; + + export type QueryWorkspaceArgs = { id: Scalars['String']['input']; }; @@ -3888,6 +3895,7 @@ export type Workspace = { projects: ProjectCollection; /** Active user's role for this workspace. `null` if request is not authenticated, or the workspace is not explicitly shared with you. */ role?: Maybe; + slug: Scalars['String']['output']; team: WorkspaceCollaboratorCollection; updatedAt: Scalars['DateTime']['output']; }; @@ -3973,6 +3981,7 @@ export type WorkspaceCreateInput = { /** Logo image as base64-encoded string */ logo?: InputMaybe; name: Scalars['String']['input']; + slug?: InputMaybe; }; export type WorkspaceDomain = { @@ -4165,7 +4174,6 @@ export type WorkspaceTeamFilter = { export type WorkspaceUpdateInput = { defaultLogoIndex?: InputMaybe; - /** stream:reviewer | stream:contributor */ defaultProjectRole?: InputMaybe; description?: InputMaybe; discoverabilityEnabled?: InputMaybe; @@ -4174,6 +4182,7 @@ export type WorkspaceUpdateInput = { /** Logo image as base64-encoded string */ logo?: InputMaybe; name?: InputMaybe; + slug?: InputMaybe; }; export type WorkspaceVersionsCount = { diff --git a/packages/server/modules/shared/test/unit/eventBus.spec.ts b/packages/server/modules/shared/test/unit/eventBus.spec.ts index 69427c28d..20987245f 100644 --- a/packages/server/modules/shared/test/unit/eventBus.spec.ts +++ b/packages/server/modules/shared/test/unit/eventBus.spec.ts @@ -8,6 +8,7 @@ import cryptoRandomString from 'crypto-random-string' const createFakeWorkspace = (): Omit => { return { id: cryptoRandomString({ length: 10 }), + slug: cryptoRandomString({ length: 10 }), description: cryptoRandomString({ length: 10 }), logo: null, defaultLogoIndex: 0, diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 75733583b..e8d97b8a9 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -32,11 +32,21 @@ export type GetWorkspace = (args: { userId?: string }) => Promise +export type GetWorkspaceBySlug = (args: { + workspaceSlug: string + userId?: string +}) => Promise + export type GetWorkspaces = (args: { workspaceIds: string[] userId?: string }) => Promise +export type GetWorkspacesBySlug = (args: { + workspaceIds: string[] + userId?: string +}) => Promise + export type StoreWorkspaceDomain = (args: { workspaceDomain: WorkspaceDomain }) => Promise diff --git a/packages/server/modules/workspaces/errors/workspace.ts b/packages/server/modules/workspaces/errors/workspace.ts index 97065e20e..e773d81aa 100644 --- a/packages/server/modules/workspaces/errors/workspace.ts +++ b/packages/server/modules/workspaces/errors/workspace.ts @@ -18,6 +18,18 @@ export class WorkspaceInvalidUpdateError extends BaseError { static statusCode = 400 } +export class WorkspaceSlugTakenError extends BaseError { + static defaultMessage = 'The given workspace slug is already taken' + static code = 'WORKSPACE_SLUG_TAKEN' + static statusCode = 400 +} + +export class WorkspaceSlugInvalidError extends BaseError { + static defaultMessage = 'The workspace slug is invalid' + static code = 'WORKSPACE_SLUG_INVALID' + static statusCode = 400 +} + export class WorkspaceInvalidRoleError extends BaseError { static defaultMessage = 'Invalid workspace role provided' static code = 'WORKSPACE_INVALID_ROLE_ERROR' diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index eb7dcfd4a..9b8930daa 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -73,7 +73,8 @@ import { countProjectsVersionsByWorkspaceIdFactory, countWorkspaceRoleWithOptionalProjectRoleFactory, getUserIdsWithRoleInWorkspaceFactory, - getWorkspaceRoleForUserFactory + getWorkspaceRoleForUserFactory, + getWorkspaceBySlugFactory } from '@/modules/workspaces/repositories/workspaces' import { buildWorkspaceInviteEmailContentsFactory, @@ -90,8 +91,10 @@ import { createWorkspaceFactory, deleteWorkspaceFactory, deleteWorkspaceRoleFactory, + generateValidSlugFactory, updateWorkspaceFactory, - updateWorkspaceRoleFactory + updateWorkspaceRoleFactory, + validateSlugFactory } from '@/modules/workspaces/services/management' import { getWorkspaceProjectsFactory, @@ -215,6 +218,13 @@ export = FF_WORKSPACES_MODULE_ENABLED token: args.token, workspaceId: args.workspaceId }) + }, + validateWorkspaceSlug: async (_parent, args) => { + const validateSlug = validateSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }) + await validateSlug({ slug: args.slug }) + return true } }, Mutation: { @@ -277,9 +287,15 @@ export = FF_WORKSPACES_MODULE_ENABLED }, WorkspaceMutations: { create: async (_parent, args, context) => { - const { name, description, defaultLogoIndex, logo } = args.input + const { name, description, defaultLogoIndex, logo, slug } = args.input const createWorkspace = createWorkspaceFactory({ + validateSlug: validateSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }), + generateValidSlug: generateValidSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }), upsertWorkspace: upsertWorkspaceFactory({ db }), upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), emitWorkspaceEvent: getEventBus().emit @@ -289,6 +305,7 @@ export = FF_WORKSPACES_MODULE_ENABLED userId: context.userId!, workspaceInput: { name, + slug, description: description ?? null, logo: logo ?? null, defaultLogoIndex: defaultLogoIndex ?? 0 @@ -331,6 +348,9 @@ export = FF_WORKSPACES_MODULE_ENABLED ) const updateWorkspace = updateWorkspaceFactory({ + validateSlug: validateSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }), getWorkspace: getWorkspaceWithDomainsFactory({ db }), upsertWorkspace: upsertWorkspaceFactory({ db }), emitWorkspaceEvent: getEventBus().emit @@ -430,6 +450,9 @@ export = FF_WORKSPACES_MODULE_ENABLED db }), updateWorkspace: updateWorkspaceFactory({ + validateSlug: validateSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }), getWorkspace: getWorkspaceWithDomainsFactory({ db }), upsertWorkspace: upsertWorkspaceFactory({ db }), emitWorkspaceEvent: getEventBus().emit diff --git a/packages/server/modules/workspaces/helpers/db.ts b/packages/server/modules/workspaces/helpers/db.ts index ba2b740e2..dc0321614 100644 --- a/packages/server/modules/workspaces/helpers/db.ts +++ b/packages/server/modules/workspaces/helpers/db.ts @@ -3,6 +3,7 @@ import { buildTableHelper } from '@/modules/core/dbSchema' export const Workspaces = buildTableHelper('workspaces', [ 'id', 'name', + 'slug', 'description', 'createdAt', 'updatedAt', diff --git a/packages/server/modules/workspaces/repositories/workspaces.ts b/packages/server/modules/workspaces/repositories/workspaces.ts index 86767b650..8cb824a1d 100644 --- a/packages/server/modules/workspaces/repositories/workspaces.ts +++ b/packages/server/modules/workspaces/repositories/workspaces.ts @@ -14,6 +14,7 @@ import { GetUserDiscoverableWorkspaces, GetUserIdsWithRoleInWorkspace, GetWorkspace, + GetWorkspaceBySlug, GetWorkspaceCollaborators, GetWorkspaceCollaboratorsTotalCount, GetWorkspaceDomains, @@ -86,6 +87,32 @@ export const getUserDiscoverableWorkspacesFactory = >[] } +const workspaceWithRoleBaseQuery = ({ + db, + userId +}: { + db: Knex + userId?: string +}): Knex.QueryBuilder => { + let q = db('workspaces') + if (userId) { + q = q + .select([ + ...Object.values(Workspaces.col), + // Getting first role from grouped results + knex.raw(`(array_agg("workspace_acl"."role"))[1] as role`) + ]) + .leftJoin(DbWorkspaceAcl.name, function () { + this.on(DbWorkspaceAcl.col.workspaceId, Workspaces.col.id).andOnVal( + DbWorkspaceAcl.col.userId, + userId + ) + }) + .groupBy(Workspaces.col.id) + } + return q +} + export const getWorkspacesFactory = ({ db }: { db: Knex }): GetWorkspaces => async (params: { @@ -96,39 +123,28 @@ export const getWorkspacesFactory = userId?: string }) => { const { workspaceIds, userId } = params - if (!workspaceIds?.length) return [] - const q = Workspaces.knex(db).whereIn( - Workspaces.col.id, - workspaceIds - ) - - if (userId) { - q.select([ - ...Object.values(Workspaces.col), - // Getting first role from grouped results - knex.raw(`(array_agg("workspace_acl"."role"))[1] as role`) - ]) - q.leftJoin(DbWorkspaceAcl.name, function () { - this.on(DbWorkspaceAcl.col.workspaceId, Workspaces.col.id).andOnVal( - DbWorkspaceAcl.col.userId, - userId - ) - }) - q.groupBy(Workspaces.col.id) - } - - const results = await q + const q = workspaceWithRoleBaseQuery({ db, userId }) + const results = await q.whereIn(Workspaces.col.id, workspaceIds) return results } export const getWorkspaceFactory = ({ db }: { db: Knex }): GetWorkspace => async ({ workspaceId, userId }) => { - const [workspace] = await getWorkspacesFactory({ db })({ - workspaceIds: [workspaceId], - userId - }) + const workspace = await workspaceWithRoleBaseQuery({ db, userId }) + .where(Workspaces.col.id, workspaceId) + .first() + + return workspace || null + } + +export const getWorkspaceBySlugFactory = + ({ db }: { db: Knex }): GetWorkspaceBySlug => + async ({ workspaceSlug, userId }) => { + const workspace = await workspaceWithRoleBaseQuery({ db, userId }) + .where(Workspaces.col.slug, workspaceSlug) + .first() return workspace || null } @@ -143,6 +159,7 @@ export const upsertWorkspaceFactory = .merge([ 'description', 'logo', + 'slug', 'defaultLogoIndex', 'defaultProjectRole', 'name', diff --git a/packages/server/modules/workspaces/services/management.ts b/packages/server/modules/workspaces/services/management.ts index bd4282b69..89b9d2627 100644 --- a/packages/server/modules/workspaces/services/management.ts +++ b/packages/server/modules/workspaces/services/management.ts @@ -10,6 +10,7 @@ import { GetWorkspaceWithDomains, GetWorkspaceDomains, UpdateWorkspace, + GetWorkspaceBySlug, UpdateWorkspaceRole } from '@/modules/workspaces/domain/operations' import { @@ -18,7 +19,12 @@ import { WorkspaceDomain, WorkspaceWithDomains } from '@/modules/workspacesCore/domain/types' -import { MaybeNullOrUndefined, Roles } from '@speckle/shared' +import { + generateSlugFromName, + MaybeNullOrUndefined, + Roles, + validateWorkspaceSlug +} from '@speckle/shared' import cryptoRandomString from 'crypto-random-string' import { deleteStream } from '@/modules/core/repositories/streams' import { @@ -33,6 +39,8 @@ import { WorkspaceProtectedError, WorkspaceUnverifiedDomainError, WorkspaceNoVerifiedDomainsError, + WorkspaceSlugTakenError, + WorkspaceSlugInvalidError, WorkspaceInvalidUpdateError } from '@/modules/workspaces/errors/workspace' import { isUserLastWorkspaceAdmin } from '@/modules/workspaces/helpers/roles' @@ -61,6 +69,7 @@ type WorkspaceCreateArgs = { userId: string workspaceInput: { name: string + slug?: string | null description: string | null logo: string | null defaultLogoIndex: number @@ -68,14 +77,58 @@ type WorkspaceCreateArgs = { userResourceAccessLimits: MaybeNullOrUndefined } +type GenerateValidSlug = (args: { name: string }) => Promise + +type ValidateWorkspaceSlug = (args: { slug: string }) => Promise + +export const validateSlugFactory = + ({ + getWorkspaceBySlug + }: { + getWorkspaceBySlug: GetWorkspaceBySlug + }): ValidateWorkspaceSlug => + async ({ slug }) => { + try { + validateWorkspaceSlug(slug) + } catch (err) { + if (err instanceof Error) throw new WorkspaceSlugInvalidError(err.message) + throw err + } + const maybeClashingWorkspace = await getWorkspaceBySlug({ + workspaceSlug: slug + }) + if (maybeClashingWorkspace) throw new WorkspaceSlugTakenError() + } + +export const generateValidSlugFactory = + ({ + getWorkspaceBySlug + }: { + getWorkspaceBySlug: GetWorkspaceBySlug + }): GenerateValidSlug => + async ({ name }) => { + const generatedSlug = generateSlugFromName({ name }) + + const maybeClashingWorkspace = await getWorkspaceBySlug({ + workspaceSlug: generatedSlug + }) + return maybeClashingWorkspace + ? `${generatedSlug}-${cryptoRandomString({ length: 5 })}` + : generatedSlug + } + export const createWorkspaceFactory = ({ upsertWorkspace, upsertWorkspaceRole, + generateValidSlug, + validateSlug, emitWorkspaceEvent }: { upsertWorkspace: UpsertWorkspace upsertWorkspaceRole: UpsertWorkspaceRole + validateSlug: ValidateWorkspaceSlug + generateValidSlug: GenerateValidSlug emitWorkspaceEvent: EventBus['emit'] }) => async ({ @@ -92,8 +145,16 @@ export const createWorkspaceFactory = throw new ForbiddenError('You are not authorized to create a workspace') } + let slug: string + if (workspaceInput.slug) { + await validateSlug({ slug: workspaceInput.slug }) + slug = workspaceInput.slug + } else { + slug = await generateValidSlug(workspaceInput) + } const workspace = { ...workspaceInput, + slug, id: cryptoRandomString({ length: 10 }), createdAt: new Date(), updatedAt: new Date(), @@ -168,10 +229,12 @@ const sanitizeInput = (input: Partial) => { export const updateWorkspaceFactory = ({ getWorkspace, + validateSlug, upsertWorkspace, emitWorkspaceEvent }: { getWorkspace: GetWorkspaceWithDomains + validateSlug: ValidateWorkspaceSlug upsertWorkspace: UpsertWorkspace emitWorkspaceEvent: EventBus['emit'] }): UpdateWorkspace => @@ -182,7 +245,6 @@ export const updateWorkspaceFactory = throw new WorkspaceNotFoundError() } - // Validate incoming changes if ( !isValidInput(workspaceInput) || !isValidWorkspace(workspaceInput, currentWorkspace) @@ -190,6 +252,8 @@ export const updateWorkspaceFactory = throw new WorkspaceInvalidUpdateError() } + if (workspaceInput.slug) await validateSlug({ slug: workspaceInput.slug }) + const workspace = { ...omit(currentWorkspace, 'domains'), ...sanitizeInput(workspaceInput), diff --git a/packages/server/modules/workspaces/tests/helpers/creation.ts b/packages/server/modules/workspaces/tests/helpers/creation.ts index 5545050b5..567be93ba 100644 --- a/packages/server/modules/workspaces/tests/helpers/creation.ts +++ b/packages/server/modules/workspaces/tests/helpers/creation.ts @@ -19,7 +19,8 @@ import { getWorkspaceFactory, getWorkspaceWithDomainsFactory, getWorkspaceDomainsFactory, - storeWorkspaceDomainFactory + storeWorkspaceDomainFactory, + getWorkspaceBySlugFactory } from '@/modules/workspaces/repositories/workspaces' import { buildWorkspaceInviteEmailContentsFactory, @@ -31,10 +32,13 @@ import { updateWorkspaceRoleFactory, deleteWorkspaceRoleFactory, updateWorkspaceFactory, - addDomainToWorkspaceFactory + addDomainToWorkspaceFactory, + validateSlugFactory, + generateValidSlugFactory } from '@/modules/workspaces/services/management' import { BasicTestUser } from '@/test/authHelper' import { CreateWorkspaceInviteMutationVariables } from '@/test/graphql/generated/graphql' +import cryptoRandomString from 'crypto-random-string' import { MaybeNullOrUndefined, Roles, @@ -51,6 +55,7 @@ export type BasicTestWorkspace = { * Leave empty, will be filled on creation */ ownerId: string + slug: string name: string description?: string logo?: string @@ -60,11 +65,17 @@ export type BasicTestWorkspace = { } export const createTestWorkspace = async ( - workspace: BasicTestWorkspace, + workspace: Omit & { slug?: string }, owner: BasicTestUser, domain?: string ) => { const createWorkspace = createWorkspaceFactory({ + validateSlug: validateSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }), + generateValidSlug: generateValidSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }), upsertWorkspace: upsertWorkspaceFactory({ db }), upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), emitWorkspaceEvent: (...args) => getEventBus().emit(...args) @@ -74,6 +85,7 @@ export const createTestWorkspace = async ( userId: owner.id, workspaceInput: { name: workspace.name, + slug: workspace.slug || cryptoRandomString({ length: 10 }), description: workspace.description || null, logo: workspace.logo || null, defaultLogoIndex: 0 @@ -100,13 +112,17 @@ export const createTestWorkspace = async ( } const updateWorkspace = updateWorkspaceFactory({ + validateSlug: validateSlugFactory({ + getWorkspaceBySlug: getWorkspaceBySlugFactory({ db }) + }), getWorkspace: getWorkspaceWithDomainsFactory({ db }), upsertWorkspace: upsertWorkspaceFactory({ db }), - emitWorkspaceEvent: getEventBus().emit + emitWorkspaceEvent: (...args) => getEventBus().emit(...args) }) if (workspace.discoverabilityEnabled) { if (!domain) throw new Error('Domain is needed for discoverability') + await updateWorkspace({ workspaceId: newWorkspace.id, workspaceInput: { diff --git a/packages/server/modules/workspaces/tests/helpers/graphql.ts b/packages/server/modules/workspaces/tests/helpers/graphql.ts index 17a65e5f6..a2406da57 100644 --- a/packages/server/modules/workspaces/tests/helpers/graphql.ts +++ b/packages/server/modules/workspaces/tests/helpers/graphql.ts @@ -4,6 +4,7 @@ export const basicWorkspaceFragment = gql` fragment BasicWorkspace on Workspace { id name + slug updatedAt createdAt role diff --git a/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts index e0e439134..3d5e39aea 100644 --- a/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts @@ -78,6 +78,7 @@ import { createRandomPassword } from '@/modules/core/helpers/testHelpers' import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/streamAccessService' import { WorkspaceProtectedError } from '@/modules/workspaces/errors/workspace' import { ForbiddenError } from '@/modules/shared/errors' +import cryptoRandomString from 'crypto-random-string' enum InviteByTarget { Email = 'email', @@ -234,6 +235,7 @@ describe('Workspaces Invites GQL', () => { name: 'My First Workspace', id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), domainBasedMembershipProtectionEnabled: false } @@ -241,12 +243,14 @@ describe('Workspaces Invites GQL', () => { name: 'My Domain protected workspace', id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), domainBasedMembershipProtectionEnabled: true } const otherGuysWorkspace: BasicTestWorkspace = { name: 'Other Guy Workspace', id: '', + slug: cryptoRandomString({ length: 10 }), ownerId: '' } @@ -587,6 +591,7 @@ describe('Workspaces Invites GQL', () => { const myProjectInviteTargetWorkspace: BasicTestWorkspace = { name: 'My Project Invite Target Workspace #1', id: '', + slug: cryptoRandomString({ length: 10 }), ownerId: '' } @@ -783,6 +788,7 @@ describe('Workspaces Invites GQL', () => { const myAdministrationWorkspace: BasicTestWorkspace = { name: 'My Administration Workspace', id: '', + slug: cryptoRandomString({ length: 10 }), ownerId: '' } @@ -908,6 +914,7 @@ describe('Workspaces Invites GQL', () => { const myInviteTargetWorkspace: BasicTestWorkspace = { name: 'My Invite Target Workspace', id: '', + slug: cryptoRandomString({ length: 10 }), ownerId: '' } const myInviteTargetWorkspaceStream1: BasicTestStream = { @@ -1122,6 +1129,7 @@ describe('Workspaces Invites GQL', () => { const brokenWorkspace: BasicTestWorkspace = { name: 'Broken Workspace', id: 'a', + slug: cryptoRandomString({ length: 10 }), ownerId: '' } await createTestWorkspaces([[brokenWorkspace, me]]) @@ -1557,6 +1565,7 @@ describe('Workspaces Invites GQL', () => { const otherWorkspace: BasicTestWorkspace = { name: 'Other Workspace', id: '', + slug: cryptoRandomString({ length: 10 }), ownerId: '' } diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 48f851588..d842783ac 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -33,6 +33,7 @@ describe('Workspace project GQL CRUD', () => { const workspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'My Test Workspace' } @@ -185,6 +186,7 @@ describe('Workspace project GQL CRUD', () => { const targetWorkspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'Target Workspace' } diff --git a/packages/server/modules/workspaces/tests/integration/repositories.spec.ts b/packages/server/modules/workspaces/tests/integration/repositories.spec.ts index e6c90d19b..a05d68848 100644 --- a/packages/server/modules/workspaces/tests/integration/repositories.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/repositories.spec.ts @@ -11,7 +11,8 @@ import { getUserDiscoverableWorkspacesFactory, getWorkspaceWithDomainsFactory, countWorkspaceRoleWithOptionalProjectRoleFactory, - getWorkspaceCollaboratorsFactory + getWorkspaceCollaboratorsFactory, + getWorkspaceBySlugFactory } from '@/modules/workspaces/repositories/workspaces' import db from '@/db/knex' import cryptoRandomString from 'crypto-random-string' @@ -40,8 +41,10 @@ import { grantStreamPermissions, upsertProjectRoleFactory } from '@/modules/core/repositories/streams' +import { omit } from 'lodash' const getWorkspace = getWorkspaceFactory({ db }) +const getWorkspaceBySlug = getWorkspaceBySlugFactory({ db }) const getWorkspaceCollaborators = getWorkspaceCollaboratorsFactory({ db }) const upsertWorkspace = upsertWorkspaceFactory({ db }) const deleteWorkspace = deleteWorkspaceFactory({ db }) @@ -77,6 +80,7 @@ const createAndStoreTestWorkspace = async ( ) => { const workspace: Omit = { id: cryptoRandomString({ length: 10 }), + slug: cryptoRandomString({ length: 10 }), name: cryptoRandomString({ length: 10 }), createdAt: new Date(), updatedAt: new Date(), @@ -105,6 +109,37 @@ describe('Workspace repositories', () => { // not testing get here, we're going to use that for testing upsert }) + describe('getWorkspaceBySlugFactory creates a function, that', () => { + it('returns null if the workspace is not found', async () => { + const workspace = await getWorkspaceBySlug({ + workspaceSlug: cryptoRandomString({ length: 10 }) + }) + expect(workspace).to.be.null + }) + it('returns the workspace', async () => { + const testUserA: BasicTestUser = { + id: '', + name: 'John A Speckle', + email: 'john@example.speckle', + role: Roles.Server.Admin + } + const testWorkspace: BasicTestWorkspace = { + id: '', + ownerId: '', + slug: cryptoRandomString({ length: 10 }), + name: 'Test Workspace' + } + + await createTestUsers([testUserA]) + await createTestWorkspace(testWorkspace, testUserA) + + const workspace = await getWorkspaceBySlug({ + workspaceSlug: testWorkspace.slug + }) + expect(workspace?.id).to.be.equal(testWorkspace.id) + }) + }) + describe('getWorkspaceCollaboratorsFactory creates a function, that', () => { const testUserA: BasicTestUser = { id: '', @@ -133,6 +168,7 @@ describe('Workspace repositories', () => { const testWorkspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'Test Workspace' } @@ -159,17 +195,20 @@ describe('Workspace repositories', () => { { id: '', ownerId: '', - name: 'Test Workspace A' + name: 'Test Workspace A', + slug: cryptoRandomString({ length: 10 }) }, { id: '', ownerId: '', - name: 'Test Workspace B' + name: 'Test Workspace B', + slug: cryptoRandomString({ length: 10 }) }, { id: '', ownerId: '', - name: 'Test Workspace C' + name: 'Test Workspace C', + slug: cryptoRandomString({ length: 10 }) } ] @@ -229,11 +268,16 @@ describe('Workspace repositories', () => { const storedWorkspace = await getWorkspace({ workspaceId: testWorkspace.id }) expect(storedWorkspace).to.deep.equal(testWorkspace) + const updateData = { + slug: cryptoRandomString({ length: 10 }), + name: cryptoRandomString({ length: 20 }), + createdAt: new Date() + } + await upsertWorkspace({ workspace: { ...testWorkspace, - id: cryptoRandomString({ length: 13 }), - createdAt: new Date() + ...updateData } }) @@ -241,7 +285,10 @@ describe('Workspace repositories', () => { workspaceId: testWorkspace.id }) - expect(modifiedStoredWorkspace).to.deep.equal(testWorkspace) + expect(modifiedStoredWorkspace).to.deep.equal({ + ...testWorkspace, + ...omit(updateData, ['createdAt']) + }) }) }) @@ -255,6 +302,7 @@ describe('Workspace repositories', () => { const workspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'Incredibly Forgettable' } @@ -753,6 +801,7 @@ describe('Workspace repositories', () => { const workspace = { id: createRandomPassword(), name: 'my workspace', + slug: cryptoRandomString({ length: 10 }), ownerId: user.id } await createTestWorkspace(workspace, user) @@ -786,6 +835,7 @@ describe('Workspace repositories', () => { const workspace = { id: createRandomPassword(), name: 'my workspace', + slug: cryptoRandomString({ length: 10 }), ownerId: admin.id } await createTestWorkspace(workspace, admin) @@ -794,6 +844,7 @@ describe('Workspace repositories', () => { const workspace2 = { id: createRandomPassword(), name: 'my workspace', + slug: cryptoRandomString({ length: 10 }), ownerId: admin.id } await createTestWorkspace(workspace2, admin) @@ -835,6 +886,7 @@ describe('Workspace repositories', () => { const workspace = { id: createRandomPassword(), name: 'my workspace', + slug: cryptoRandomString({ length: 10 }), ownerId: admin.id } @@ -906,6 +958,7 @@ describe('Workspace repositories', () => { const workspace = { id: createRandomPassword(), name: 'my workspace', + slug: cryptoRandomString({ length: 10 }), ownerId: admin.id } await createTestWorkspace(workspace, admin) @@ -983,6 +1036,7 @@ describe('Workspace repositories', () => { const workspace1 = { id: createRandomPassword(), name: 'my workspace', + slug: cryptoRandomString({ length: 10 }), ownerId: admin.id } await createTestWorkspace(workspace1, admin) @@ -990,6 +1044,7 @@ describe('Workspace repositories', () => { const workspace2 = { id: createRandomPassword(), name: 'my workspace 2', + slug: cryptoRandomString({ length: 10 }), ownerId: admin.id } await createTestWorkspace(workspace2, admin) diff --git a/packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts index ad17089a1..8bdc06c1d 100644 --- a/packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/roles.graph.spec.ts @@ -26,6 +26,7 @@ import { beforeEachContext, truncateTables } from '@/test/hooks' import { BasicTestStream, createTestStream } from '@/test/speckle-helpers/streamHelper' import { Roles } from '@speckle/shared' import { expect } from 'chai' +import cryptoRandomString from 'crypto-random-string' import { isUndefined } from 'lodash' describe('Workspaces Roles GQL', () => { @@ -64,6 +65,7 @@ describe('Workspaces Roles GQL', () => { const workspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'My Test Workspace' } @@ -187,6 +189,7 @@ describe('Workspaces Roles GQL', () => { const workspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'Test Workspace', defaultProjectRole: Roles.Stream.Reviewer } @@ -531,6 +534,7 @@ describe('Workspaces Roles GQL', () => { const workspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'Test Workspace w/ Projects' } diff --git a/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts index 0096b1399..8fb987993 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts @@ -138,7 +138,8 @@ describe('Workspaces GQL CRUD', () => { const workspace: BasicTestWorkspace = { id: '', ownerId: '', - name: 'Workspace A' + name: 'Workspace A', + slug: cryptoRandomString({ length: 10 }) } const testMemberUser: BasicTestUser = { @@ -188,6 +189,7 @@ describe('Workspaces GQL CRUD', () => { id: '', ownerId: '', name: 'My Large Workspace', + slug: cryptoRandomString({ length: 10 }), description: 'A workspace with many users and roles to test pagination.' } @@ -529,7 +531,10 @@ describe('Workspaces GQL CRUD', () => { it('should return workspace cost', async () => { const createRes = await apollo.execute(CreateWorkspaceDocument, { - input: { name: createRandomString() } + input: { + name: createRandomString(), + slug: cryptoRandomString({ length: 10 }) + } }) expect(createRes).to.not.haveGraphQLErrors() const workspaceId = createRes.data!.workspaceMutations.create.id @@ -666,6 +671,7 @@ describe('Workspaces GQL CRUD', () => { const workspace = { id: '', name: 'test ws', + slug: cryptoRandomString({ length: 10 }), ownerId: '' } await createTestWorkspace(workspace, testMemberUser) @@ -710,9 +716,10 @@ describe('Workspaces GQL CRUD', () => { describe('mutation workspaceMutations.create', () => { it('should create a workspace', async () => { const workspaceName = cryptoRandomString({ length: 6 }) + const workspaceSlug = cryptoRandomString({ length: 10 }) const createRes = await apollo.execute(CreateWorkspaceDocument, { - input: { name: workspaceName } + input: { name: workspaceName, slug: workspaceSlug } }) const getRes = await apollo.execute(GetWorkspaceDocument, { workspaceId: createRes.data!.workspaceMutations.create.id @@ -722,6 +729,7 @@ describe('Workspaces GQL CRUD', () => { expect(getRes).to.not.haveGraphQLErrors() expect(getRes.data?.workspace).to.exist expect(getRes.data?.workspace?.name).to.equal(workspaceName) + expect(getRes.data?.workspace?.slug).to.equal(workspaceSlug) }) }) @@ -729,6 +737,7 @@ describe('Workspaces GQL CRUD', () => { const workspace: BasicTestWorkspace = { id: '', ownerId: '', + slug: cryptoRandomString({ length: 10 }), name: 'My Test Workspace' } @@ -825,7 +834,7 @@ describe('Workspaces GQL CRUD', () => { }) describe('mutation workspaceMutations.update', () => { - const workspace: BasicTestWorkspace = { + const workspace = { id: '', ownerId: '', name: cryptoRandomString({ length: 6 }), @@ -927,7 +936,7 @@ describe('Workspaces GQL CRUD', () => { it('allows the active user to leave a workspace', async () => { const name = cryptoRandomString({ length: 6 }) const workspaceCreateResult = await apollo.execute(CreateWorkspaceDocument, { - input: { name } + input: { name, slug: cryptoRandomString({ length: 10 }) } }) expect(workspaceCreateResult).to.not.haveGraphQLErrors() @@ -970,7 +979,7 @@ describe('Workspaces GQL CRUD', () => { it('stops the last workspace admin from leaving the workspace', async () => { const name = cryptoRandomString({ length: 6 }) const workspaceCreateResult = await apollo.execute(CreateWorkspaceDocument, { - input: { name } + input: { name, slug: cryptoRandomString({ length: 10 }) } }) const id = workspaceCreateResult.data?.workspaceMutations.create.id @@ -1000,7 +1009,7 @@ describe('Workspaces GQL CRUD', () => { const workspaceName = cryptoRandomString({ length: 6 }) const createRes = await apollo.execute(CreateWorkspaceDocument, { - input: { name: workspaceName } + input: { name: workspaceName, slug: cryptoRandomString({ length: 10 }) } }) expect(createRes).to.not.haveGraphQLErrors() const workspaceId = createRes.data!.workspaceMutations.create.id diff --git a/packages/server/modules/workspaces/tests/unit/services/domains.spec.ts b/packages/server/modules/workspaces/tests/unit/services/domains.spec.ts index dccb153a1..3b96def98 100644 --- a/packages/server/modules/workspaces/tests/unit/services/domains.spec.ts +++ b/packages/server/modules/workspaces/tests/unit/services/domains.spec.ts @@ -24,6 +24,7 @@ describe('workspace domain services', () => { defaultLogoIndex: 0, name: cryptoRandomString({ length: 10 }), logo: null, + slug: cryptoRandomString({ length: 10 }), createdAt: new Date(), updatedAt: new Date(), description: '', @@ -47,6 +48,7 @@ describe('workspace domain services', () => { defaultLogoIndex: 0, name: cryptoRandomString({ length: 10 }), logo: null, + slug: cryptoRandomString({ length: 10 }), createdAt: new Date(), updatedAt: new Date(), description: '', diff --git a/packages/server/modules/workspaces/tests/unit/services/join.spec.ts b/packages/server/modules/workspaces/tests/unit/services/join.spec.ts index e0d64ccf8..2b3fdfdef 100644 --- a/packages/server/modules/workspaces/tests/unit/services/join.spec.ts +++ b/packages/server/modules/workspaces/tests/unit/services/join.spec.ts @@ -24,6 +24,7 @@ const createTestWorkspaceWithDomains = ( createdAt: new Date(), updatedAt: new Date(), name: createRandomPassword(), + slug: createRandomPassword(), description: createRandomPassword(), id: createRandomPassword(), logo: null, diff --git a/packages/server/modules/workspaces/tests/unit/services/management.spec.ts b/packages/server/modules/workspaces/tests/unit/services/management.spec.ts index 88369dbb1..74cff4e41 100644 --- a/packages/server/modules/workspaces/tests/unit/services/management.spec.ts +++ b/packages/server/modules/workspaces/tests/unit/services/management.spec.ts @@ -8,10 +8,12 @@ import { addDomainToWorkspaceFactory, createWorkspaceFactory, deleteWorkspaceRoleFactory, + generateValidSlugFactory, updateWorkspaceFactory, - updateWorkspaceRoleFactory + updateWorkspaceRoleFactory, + validateSlugFactory } from '@/modules/workspaces/services/management' -import { Roles } from '@speckle/shared' +import { Roles, validateWorkspaceSlug } from '@speckle/shared' import { expect } from 'chai' import cryptoRandomString from 'crypto-random-string' import { @@ -27,6 +29,8 @@ import { WorkspaceNotFoundError, WorkspaceNoVerifiedDomainsError, WorkspaceProtectedError, + WorkspaceSlugInvalidError, + WorkspaceSlugTakenError, WorkspaceUnverifiedDomainError } from '@/modules/workspaces/errors/workspace' import { UserEmail } from '@/modules/core/domain/userEmails/types' @@ -66,6 +70,8 @@ const buildCreateWorkspaceWithTestContext = ( }) => { context.storedWorkspaces.push(workspace) }, + validateSlug: async () => {}, + generateValidSlug: async () => cryptoRandomString({ length: 10 }), upsertWorkspaceRole: async (workspaceAcl: WorkspaceAcl) => { context.storedRoles.push(workspaceAcl) }, @@ -88,6 +94,7 @@ const getCreateWorkspaceInput = () => { userId: cryptoRandomString({ length: 10 }), workspaceInput: { description: 'foobar', + slug: cryptoRandomString({ length: 10 }), logo: null, name: cryptoRandomString({ length: 6 }), defaultLogoIndex: 0 @@ -96,19 +103,117 @@ const getCreateWorkspaceInput = () => { } describe('Workspace services', () => { + describe('isSlugValid', () => { + it('throws for url unsafe characters', async () => { + const err = await expectToThrow(() => { + validateWorkspaceSlug('{{{}}}}}') + }) + expect(err.message).to.contain('only lowercase letters, numbers') + }) + it('throws for too short inputs', async () => { + const err = await expectToThrow(() => { + validateWorkspaceSlug('{') + }) + expect(err.message).to.contain('characters long.') + }) + it('throws for too long inputs', async () => { + const err = await expectToThrow(() => { + validateWorkspaceSlug(cryptoRandomString({ length: 31 })) + }) + expect(err.message).to.contain('slug must not exceed') + }) + it('throws for invalid start', async () => { + const err = await expectToThrow(() => { + validateWorkspaceSlug('-asdfasdf-') + }) + expect(err.message).to.contain('cannot start or end with a') + }) + it('returns true for valid slugs', () => { + validateWorkspaceSlug('asdf-asdf') + // if it did not throw, we're good + expect(true) + }) + }) + describe('validateSlugFactory creates a function, that', () => { + it('throws WorkspaceSlugTakenError if the input slug clashes an existing workspace', async () => { + const validateSlug = validateSlugFactory({ + getWorkspaceBySlug: async () => + ({ id: cryptoRandomString({ length: 10 }) } as Workspace) + }) + + const err = await expectToThrow(async () => { + await validateSlug({ + slug: cryptoRandomString({ length: 10 }) + }) + }) + expect(err.message).to.be.equal(new WorkspaceSlugTakenError().message) + }) + it('throws validation error for invalid slugs', async () => { + const validateSlug = validateSlugFactory({ + getWorkspaceBySlug: async () => null + }) + + const err = await expectToThrow(async () => { + await validateSlug({ + slug: '-----' + }) + }) + expect(err.message).to.contain('cannot start or end with a hyphen') + }) + }) + + describe('generateValidSlugFactory creates a function, that', () => { + it('generates a slug from the input name', async () => { + const slug = await generateValidSlugFactory({ + getWorkspaceBySlug: async () => null + })({ name: 'Foo bAr{ }baZ' }) + expect(slug).to.be.equal('foo-bar-baz') + }) + it('adds a random string to the generated slug if it clashes an existing one', async () => { + const slug = await generateValidSlugFactory({ + getWorkspaceBySlug: async () => + ({ id: cryptoRandomString({ length: 10 }) } as Workspace) + })({ name: 'FoobAr' }) + expect(slug).contain('foobar-') + expect(slug.length).to.be.equal(12) + }) + }) describe('createWorkspaceFactory creates a function, that', () => { - it('stores the workspace', async () => { - const { context, createWorkspace } = buildCreateWorkspaceWithTestContext() + it('throws WorkspaceSlugInvalidError if the input slug is not valid', async () => { + const { createWorkspace } = buildCreateWorkspaceWithTestContext({ + validateSlug: async () => { + throw new WorkspaceSlugInvalidError() + } + }) const { userId, workspaceInput } = getCreateWorkspaceInput() + const err = await expectToThrow(async () => { + await createWorkspace({ + userId, + workspaceInput: { ...workspaceInput, slug: 'asdf{{}}}' }, + userResourceAccessLimits: null + }) + }) + expect(err.message).to.be.equal(new WorkspaceSlugInvalidError().message) + }) + it('generates a workspace slug from the workspace name', async () => { + const generatedSlug = cryptoRandomString({ length: 10 }) + const { userId, workspaceInput } = getCreateWorkspaceInput() + const { context, createWorkspace } = buildCreateWorkspaceWithTestContext({ + generateValidSlug: async () => generatedSlug + }) + const workspace = await createWorkspace({ userId, - workspaceInput, + workspaceInput: { ...workspaceInput, slug: null }, userResourceAccessLimits: null }) expect(context.storedWorkspaces.length).to.equal(1) - expect(context.storedWorkspaces[0]).to.deep.equal(omit(workspace, 'domains')) + expect(omit(context.storedWorkspaces[0], 'slug')).to.deep.equal( + omit(workspace, 'domains', 'slug') + ) + expect(context.storedWorkspaces[0].slug).to.equal(generatedSlug) }) it('makes the workspace creator becomes a workspace:admin', async () => { const { context, createWorkspace } = buildCreateWorkspaceWithTestContext() @@ -150,6 +255,7 @@ describe('Workspace services', () => { const workspaceId = cryptoRandomString({ length: 10 }) const workspace: WorkspaceWithDomains = { id: workspaceId, + slug: cryptoRandomString({ length: 10 }), name: cryptoRandomString({ length: 10 }), description: cryptoRandomString({ length: 20 }), createdAt: new Date(), @@ -167,6 +273,9 @@ describe('Workspace services', () => { const err = await expectToThrow(async () => { await updateWorkspaceFactory({ getWorkspace: async () => null, + validateSlug: async () => { + expect.fail() + }, emitWorkspaceEvent: async () => { expect.fail() }, @@ -188,6 +297,9 @@ describe('Workspace services', () => { emitWorkspaceEvent: async () => { expect.fail() }, + validateSlug: async () => { + expect.fail() + }, upsertWorkspace: async () => { expect.fail() } @@ -208,6 +320,9 @@ describe('Workspace services', () => { emitWorkspaceEvent: async () => { expect.fail() }, + validateSlug: async () => { + expect.fail() + }, upsertWorkspace: async () => { expect.fail() } @@ -220,6 +335,29 @@ describe('Workspace services', () => { }) expect(err.message).to.be.equal('Provided logo is malformed') }) + it('validates description length', async () => { + const workspace = createTestWorkspaceWithDomainsData() + const err = await expectToThrow(async () => { + await updateWorkspaceFactory({ + getWorkspace: async () => workspace, + emitWorkspaceEvent: async () => { + expect.fail() + }, + validateSlug: async () => { + throw new WorkspaceSlugInvalidError() + }, + upsertWorkspace: async () => { + expect.fail() + } + })({ + workspaceId: workspace.id, + workspaceInput: { + slug: '{}{}{}{}' + } + }) + }) + expect(err.message).to.be.equal(new WorkspaceSlugInvalidError().message) + }) it('does not allow turning on discoverability if the workspace has no verified domains', async () => { const workspace = createTestWorkspaceWithDomainsData() const err = await expectToThrow(async () => { @@ -228,6 +366,7 @@ describe('Workspace services', () => { emitWorkspaceEvent: async () => { expect.fail() }, + validateSlug: async () => {}, upsertWorkspace: async () => { expect.fail() } @@ -249,6 +388,7 @@ describe('Workspace services', () => { emitWorkspaceEvent: async () => { expect.fail() }, + validateSlug: async () => {}, upsertWorkspace: async () => { expect.fail() } @@ -271,6 +411,8 @@ describe('Workspace services', () => { emitWorkspaceEvent: async () => { return [] }, + validateSlug: async () => {}, + upsertWorkspace: async ({ workspace }) => { newWorkspaceName = workspace.name } @@ -309,6 +451,7 @@ describe('Workspace services', () => { emitWorkspaceEvent: async () => { return [] }, + validateSlug: async () => {}, upsertWorkspace: async ({ workspace }) => { updatedWorkspace = workspace } @@ -932,6 +1075,7 @@ describe('Workspace role services', () => { userId, id: workspaceId, name: cryptoRandomString({ length: 10 }), + slug: cryptoRandomString({ length: 10 }), logo: null, createdAt: new Date(), updatedAt: new Date(), @@ -975,6 +1119,7 @@ describe('Workspace role services', () => { const workspace: Workspace = { id: workspaceId, name: cryptoRandomString({ length: 10 }), + slug: cryptoRandomString({ length: 10 }), logo: null, createdAt: new Date(), updatedAt: new Date(), @@ -1030,6 +1175,7 @@ describe('Workspace role services', () => { const workspace: Workspace = { id: workspaceId, name: cryptoRandomString({ length: 10 }), + slug: cryptoRandomString({ length: 10 }), logo: null, createdAt: new Date(), updatedAt: new Date(), @@ -1091,6 +1237,7 @@ describe('Workspace role services', () => { const workspaceWithoutDomains = { id: workspaceId, name: cryptoRandomString({ length: 10 }), + slug: cryptoRandomString({ length: 10 }), logo: null, createdAt: new Date(), updatedAt: new Date(), diff --git a/packages/server/modules/workspacesCore/domain/types.ts b/packages/server/modules/workspacesCore/domain/types.ts index c8e42c02c..57659f73e 100644 --- a/packages/server/modules/workspacesCore/domain/types.ts +++ b/packages/server/modules/workspacesCore/domain/types.ts @@ -22,6 +22,7 @@ export type WorkspaceInviteResourceTarget = InviteResourceTarget< export type Workspace = { id: string name: string + slug: string description: string | null createdAt: Date updatedAt: Date diff --git a/packages/server/modules/workspacesCore/migrations/20240912134548_add_workspace_slug.ts b/packages/server/modules/workspacesCore/migrations/20240912134548_add_workspace_slug.ts new file mode 100644 index 000000000..c5ccac9cd --- /dev/null +++ b/packages/server/modules/workspacesCore/migrations/20240912134548_add_workspace_slug.ts @@ -0,0 +1,17 @@ +import { Knex } from 'knex' + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable('workspaces', (table) => { + table + .text('slug') + .notNullable() + .defaultTo(knex.raw('substring(md5(random()::text), 0, 15)')) // lets generate a random thing here to make it not nullable + .unique() // this also adds an index to the col + }) +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable('workspaces', (table) => { + table.dropColumn('slug') + }) +} diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 5d2f0c0b3..ead72bb73 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -2492,6 +2492,8 @@ export type Query = { * The query looks for matches in name & email */ userSearch: UserSearchResultCollection; + /** Validates the slug, to make sure it contains only valid characters and its not taken. */ + validateWorkspaceSlug: Scalars['Boolean']['output']; workspace: Workspace; /** * Look for an invitation to a workspace, for the current user (authed or not). @@ -2627,6 +2629,11 @@ export type QueryUserSearchArgs = { }; +export type QueryValidateWorkspaceSlugArgs = { + slug: Scalars['String']['input']; +}; + + export type QueryWorkspaceArgs = { id: Scalars['String']['input']; }; @@ -3889,6 +3896,7 @@ export type Workspace = { projects: ProjectCollection; /** Active user's role for this workspace. `null` if request is not authenticated, or the workspace is not explicitly shared with you. */ role?: Maybe; + slug: Scalars['String']['output']; team: WorkspaceCollaboratorCollection; updatedAt: Scalars['DateTime']['output']; }; @@ -3974,6 +3982,7 @@ export type WorkspaceCreateInput = { /** Logo image as base64-encoded string */ logo?: InputMaybe; name: Scalars['String']['input']; + slug?: InputMaybe; }; export type WorkspaceDomain = { @@ -4166,7 +4175,6 @@ export type WorkspaceTeamFilter = { export type WorkspaceUpdateInput = { defaultLogoIndex?: InputMaybe; - /** stream:reviewer | stream:contributor */ defaultProjectRole?: InputMaybe; description?: InputMaybe; discoverabilityEnabled?: InputMaybe; @@ -4175,6 +4183,7 @@ export type WorkspaceUpdateInput = { /** Logo image as base64-encoded string */ logo?: InputMaybe; name?: InputMaybe; + slug?: InputMaybe; }; export type WorkspaceVersionsCount = { @@ -4192,7 +4201,7 @@ export type CreateObjectMutationVariables = Exact<{ export type CreateObjectMutation = { __typename?: 'Mutation', objectCreate: Array }; -export type BasicWorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, updatedAt: string, createdAt: string, role?: string | null }; +export type BasicWorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null }; export type BasicPendingWorkspaceCollaboratorFragment = { __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }; @@ -4206,7 +4215,7 @@ export type CreateWorkspaceInviteMutationVariables = Exact<{ }>; -export type CreateWorkspaceInviteMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', invites: { __typename?: 'WorkspaceInviteMutations', create: { __typename?: 'Workspace', id: string, name: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } } } }; +export type CreateWorkspaceInviteMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', invites: { __typename?: 'WorkspaceInviteMutations', create: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } } } }; export type BatchCreateWorkspaceInvitesMutationVariables = Exact<{ workspaceId: Scalars['String']['input']; @@ -4214,28 +4223,28 @@ export type BatchCreateWorkspaceInvitesMutationVariables = Exact<{ }>; -export type BatchCreateWorkspaceInvitesMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', invites: { __typename?: 'WorkspaceInviteMutations', batchCreate: { __typename?: 'Workspace', id: string, name: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } } } }; +export type BatchCreateWorkspaceInvitesMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', invites: { __typename?: 'WorkspaceInviteMutations', batchCreate: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } } } }; export type GetWorkspaceWithTeamQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; }>; -export type GetWorkspaceWithTeamQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } }; +export type GetWorkspaceWithTeamQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } }; export type GetWorkspaceWithBillingQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; }>; -export type GetWorkspaceWithBillingQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, updatedAt: string, createdAt: string, role?: string | null, billing?: { __typename?: 'WorkspaceBilling', versionsCount: { __typename?: 'WorkspaceVersionsCount', current: number, max: number }, cost: { __typename?: 'WorkspaceCost', subTotal: number, currency: Currency, total: number, items: Array<{ __typename?: 'WorkspaceCostItem', count: number, name: string, cost: number, label: string }>, discount?: { __typename?: 'WorkspaceCostDiscount', name: string, amount: number } | null } } | null } }; +export type GetWorkspaceWithBillingQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, billing?: { __typename?: 'WorkspaceBilling', versionsCount: { __typename?: 'WorkspaceVersionsCount', current: number, max: number }, cost: { __typename?: 'WorkspaceCost', subTotal: number, currency: Currency, total: number, items: Array<{ __typename?: 'WorkspaceCostItem', count: number, name: string, cost: number, label: string }>, discount?: { __typename?: 'WorkspaceCostDiscount', name: string, amount: number } | null } } | null } }; export type GetWorkspaceWithProjectsQueryVariables = Exact<{ workspaceId: Scalars['String']['input']; }>; -export type GetWorkspaceWithProjectsQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, updatedAt: string, createdAt: string, role?: string | null, projects: { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string }> } } }; +export type GetWorkspaceWithProjectsQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, projects: { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string }> } } }; export type CancelWorkspaceInviteMutationVariables = Exact<{ workspaceId: Scalars['String']['input']; @@ -4243,7 +4252,7 @@ export type CancelWorkspaceInviteMutationVariables = Exact<{ }>; -export type CancelWorkspaceInviteMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', invites: { __typename?: 'WorkspaceInviteMutations', cancel: { __typename?: 'Workspace', id: string, name: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } } } }; +export type CancelWorkspaceInviteMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', invites: { __typename?: 'WorkspaceInviteMutations', cancel: { __typename?: 'Workspace', id: string, name: string, slug: string, updatedAt: string, createdAt: string, role?: string | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, workspaceId: string, workspaceName: string, title: string, role: string, token?: string | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string }, user?: { __typename?: 'LimitedUser', id: string, name: string } | null }> | null } } } }; export type UseWorkspaceInviteMutationVariables = Exact<{ input: WorkspaceInviteUseInput; @@ -4800,7 +4809,7 @@ export type MarkProjectVersionReceivedMutationVariables = Exact<{ export type MarkProjectVersionReceivedMutation = { __typename?: 'Mutation', versionMutations: { __typename?: 'VersionMutations', markReceived: boolean } }; -export type TestWorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null }; +export type TestWorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, slug: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null }; export type TestWorkspaceCollaboratorFragment = { __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', name: string }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }; @@ -4811,7 +4820,7 @@ export type CreateWorkspaceMutationVariables = Exact<{ }>; -export type CreateWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', create: { __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null } } }; +export type CreateWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', create: { __typename?: 'Workspace', id: string, name: string, slug: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null } } }; export type DeleteWorkspaceMutationVariables = Exact<{ workspaceId: Scalars['String']['input']; @@ -4825,7 +4834,7 @@ export type GetWorkspaceQueryVariables = Exact<{ }>; -export type GetWorkspaceQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', name: string }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }> } } }; +export type GetWorkspaceQuery = { __typename?: 'Query', workspace: { __typename?: 'Workspace', id: string, name: string, slug: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', name: string }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }> } } }; export type GetActiveUserDiscoverableWorkspacesQueryVariables = Exact<{ [key: string]: never; }>; @@ -4837,12 +4846,12 @@ export type UpdateWorkspaceMutationVariables = Exact<{ }>; -export type UpdateWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', update: { __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null } } }; +export type UpdateWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', update: { __typename?: 'Workspace', id: string, name: string, slug: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null } } }; export type GetActiveUserWorkspacesQueryVariables = Exact<{ [key: string]: never; }>; -export type GetActiveUserWorkspacesQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', workspaces: { __typename?: 'WorkspaceCollection', items: Array<{ __typename?: 'Workspace', id: string, name: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null }> } } | null }; +export type GetActiveUserWorkspacesQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', workspaces: { __typename?: 'WorkspaceCollection', items: Array<{ __typename?: 'Workspace', id: string, name: string, slug: string, description?: string | null, createdAt: string, updatedAt: string, logo?: string | null }> } } | null }; export type UpdateWorkspaceRoleMutationVariables = Exact<{ input: WorkspaceRoleUpdateInput; @@ -4898,7 +4907,7 @@ export type MoveProjectToWorkspaceMutationVariables = Exact<{ export type MoveProjectToWorkspaceMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', projects: { __typename?: 'WorkspaceProjectMutations', moveToWorkspace: { __typename?: 'Project', id: string, workspaceId?: string | null, team: Array<{ __typename?: 'ProjectCollaborator', id: string, role: string }> } } } }; -export const BasicWorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode; +export const BasicWorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode; export const BasicPendingWorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; export const WorkspaceBillingFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBilling"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billing"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionsCount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"current"}},{"kind":"Field","name":{"kind":"Name","value":"max"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cost"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subTotal"}},{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"cost"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"discount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceProjectsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjects"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]} as unknown as DocumentNode; @@ -4914,16 +4923,16 @@ export const BasicStreamFieldsFragmentDoc = {"kind":"Document","definitions":[{" export const UserWithEmailsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserWithEmails"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"emails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"primary"}}]}}]}}]} as unknown as DocumentNode; export const BaseUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode; export const BaseLimitedUserFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseLimitedUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}}]} as unknown as DocumentNode; -export const TestWorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; +export const TestWorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; export const TestWorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"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":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"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 TestWorkspaceProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]} as unknown as DocumentNode; export const CreateObjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateObject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ObjectCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objectCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"objectInput"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; -export const CreateWorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspaceInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; -export const BatchCreateWorkspaceInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BatchCreateWorkspaceInvites"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteCreateInput"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"batchCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; -export const GetWorkspaceWithTeamDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithTeam"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; -export const GetWorkspaceWithBillingDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithBilling"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBilling"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBilling"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billing"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionsCount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"current"}},{"kind":"Field","name":{"kind":"Name","value":"max"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cost"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subTotal"}},{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"cost"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"discount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetWorkspaceWithProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjects"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjects"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]} as unknown as DocumentNode; -export const CancelWorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CancelWorkspaceInvite"},"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":"inviteId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cancel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; +export const CreateWorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspaceInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; +export const BatchCreateWorkspaceInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BatchCreateWorkspaceInvites"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteCreateInput"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"batchCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; +export const GetWorkspaceWithTeamDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithTeam"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; +export const GetWorkspaceWithBillingDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithBilling"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBilling"}}]}}]}},{"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":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBilling"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billing"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionsCount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"current"}},{"kind":"Field","name":{"kind":"Name","value":"max"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cost"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subTotal"}},{"kind":"Field","name":{"kind":"Name","value":"currency"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"count"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"cost"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}},{"kind":"Field","name":{"kind":"Name","value":"discount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"total"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetWorkspaceWithProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceWithProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjects"}}]}}]}}]}},{"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":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjects"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]} as unknown as DocumentNode; +export const CancelWorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CancelWorkspaceInvite"},"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":"inviteId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cancel"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]} as unknown as DocumentNode; export const UseWorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UseWorkspaceInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteUseInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"invites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"use"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]}}]} as unknown as DocumentNode; export const GetWorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceInvite"},"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":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}},{"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 GetMyWorkspaceInvitesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMyWorkspaceInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceInvites"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicPendingWorkspaceCollaborator"}}]}}]}}]}},{"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; @@ -4997,12 +5006,12 @@ export const GetPendingEmailVerificationStatusDocument = {"kind":"Document","def export const RequestVerificationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RequestVerification"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"requestVerification"}}]}}]} as unknown as DocumentNode; export const CreateProjectVersionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProjectVersion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateVersionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}}]}}]}}]}}]} as unknown as DocumentNode; export const MarkProjectVersionReceivedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkProjectVersionReceived"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MarkReceivedVersionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"versionMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markReceived"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; -export const CreateWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; +export const CreateWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; export const DeleteWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"delete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}]}]}}]}}]} as unknown as DocumentNode; -export const GetWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceCollaborator"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"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":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"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 GetWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceCollaborator"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"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":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"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 GetActiveUserDiscoverableWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getActiveUserDiscoverableWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"discoverableWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode; -export const UpdateWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; -export const GetActiveUserWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; +export const UpdateWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; +export const GetActiveUserWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}}]}}]} as unknown as DocumentNode; export const UpdateWorkspaceRoleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspaceRole"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceRoleUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateRole"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"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":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"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 CreateWorkspaceProjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateWorkspaceProject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceProject"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]} as unknown as DocumentNode; export const GetWorkspaceProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceProjectsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceProject"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/server/test/graphql/workspaces.ts b/packages/server/test/graphql/workspaces.ts index 6c1d58854..fa61cd25a 100644 --- a/packages/server/test/graphql/workspaces.ts +++ b/packages/server/test/graphql/workspaces.ts @@ -4,6 +4,7 @@ export const workspaceFragment = gql` fragment TestWorkspace on Workspace { id name + slug description createdAt updatedAt diff --git a/packages/shared/src/workspaces/errors/index.ts b/packages/shared/src/workspaces/errors/index.ts index 8aaf21b54..36c6df8cd 100644 --- a/packages/shared/src/workspaces/errors/index.ts +++ b/packages/shared/src/workspaces/errors/index.ts @@ -10,6 +10,13 @@ export class InvalidWorkspaceSlugError extends Error { } } +export const generateSlugFromName = ({ name }: { name: string }): string => { + return name + .replace(/ /g, '-') + .toLowerCase() + .replace(/[^a-z0-9-]/g, '') +} + export function validateWorkspaceSlug(slug: string): void { if (slug.length < MIN_SLUG_LENGTH) { throw new InvalidWorkspaceSlugError(