diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 294c0ccc47..fc40cec300 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -226,60 +226,6 @@ input PendingWorkspaceCollaboratorsFilter { search: String } -type WorkspaceVersionsCount { - """ - Total number of versions of all projects in the workspace - """ - current: Int! - """ - Maximum number of version of all projects in the workspace with no additional cost - """ - max: Int! -} - -enum Currency { - GBP - USD - EUR -} - -type WorkspaceCostItem { - count: Int! - name: String! - cost: Float! - label: String! -} - -type WorkspaceCostDiscount { - name: String! - amount: Float! -} - -type WorkspaceCost { - """ - Estimated cost of the workspace with no discount applied - """ - subTotal: Float! - """ - Currency of the price - """ - currency: Currency! - items: [WorkspaceCostItem!]! - """ - Discount applied to the total - """ - discount: WorkspaceCostDiscount - """ - Total cost with discount applied - """ - total: Float! -} - -type WorkspaceBilling { - versionsCount: WorkspaceVersionsCount! - cost: WorkspaceCost! -} - type Workspace { id: ID! name: String! @@ -324,10 +270,6 @@ type Workspace { filter: WorkspaceProjectsFilter ): ProjectCollection! """ - Billing data for Workspaces beta - """ - billing: WorkspaceBilling @hasWorkspaceRole(role: MEMBER) - """ Information about the workspace's SSO configuration and the current user's SSO session, if present """ sso: WorkspaceSso diff --git a/packages/server/codegen.yml b/packages/server/codegen.yml index 8aa726e4f3..773038547a 100644 --- a/packages/server/codegen.yml +++ b/packages/server/codegen.yml @@ -56,7 +56,6 @@ generates: ProjectAutomationsUpdatedMessage: '@/modules/automate/helpers/graphTypes#ProjectAutomationsUpdatedMessageGraphQLReturn' UserAutomateInfo: '@/modules/automate/helpers/graphTypes#UserAutomateInfoGraphQLReturn' Workspace: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceGraphQLReturn' - WorkspaceBilling: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceBillingGraphQLReturn' WorkspaceSso: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceSsoGraphQLReturn' WorkspaceMutations: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceMutationsGraphQLReturn' WorkspaceInviteMutations: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceInviteMutationsGraphQLReturn' diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index f88cd3bb86..ab898e5d30 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -5,7 +5,7 @@ import { CommentReplyAuthorCollectionGraphQLReturn, CommentGraphQLReturn } from import { PendingStreamCollaboratorGraphQLReturn } from '@/modules/serverinvites/helpers/graphTypes'; import { FileUploadGraphQLReturn } from '@/modules/fileuploads/helpers/types'; import { AutomateFunctionGraphQLReturn, AutomateFunctionReleaseGraphQLReturn, AutomationGraphQLReturn, AutomationRevisionGraphQLReturn, AutomationRevisionFunctionGraphQLReturn, AutomateRunGraphQLReturn, AutomationRunTriggerGraphQLReturn, AutomationRevisionTriggerDefinitionGraphQLReturn, AutomateFunctionRunGraphQLReturn, TriggeredAutomationsStatusGraphQLReturn, ProjectAutomationMutationsGraphQLReturn, ProjectTriggeredAutomationsStatusUpdatedMessageGraphQLReturn, ProjectAutomationsUpdatedMessageGraphQLReturn, UserAutomateInfoGraphQLReturn } from '@/modules/automate/helpers/graphTypes'; -import { WorkspaceGraphQLReturn, WorkspaceBillingGraphQLReturn, WorkspaceSsoGraphQLReturn, WorkspaceMutationsGraphQLReturn, WorkspaceInviteMutationsGraphQLReturn, WorkspaceProjectMutationsGraphQLReturn, PendingWorkspaceCollaboratorGraphQLReturn, WorkspaceCollaboratorGraphQLReturn, ProjectRoleGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes'; +import { WorkspaceGraphQLReturn, WorkspaceSsoGraphQLReturn, WorkspaceMutationsGraphQLReturn, WorkspaceInviteMutationsGraphQLReturn, WorkspaceProjectMutationsGraphQLReturn, PendingWorkspaceCollaboratorGraphQLReturn, WorkspaceCollaboratorGraphQLReturn, ProjectRoleGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes'; import { WorkspaceBillingMutationsGraphQLReturn } from '@/modules/gatekeeper/helpers/graphTypes'; import { WebhookGraphQLReturn } from '@/modules/webhooks/helpers/graphTypes'; import { SmartTextEditorValueGraphQLReturn } from '@/modules/core/services/richTextEditorService'; @@ -4029,8 +4029,6 @@ export type Workspace = { __typename?: 'Workspace'; /** Regions available to the workspace for project data residency */ availableRegions: Array; - /** Billing data for Workspaces beta */ - billing?: Maybe; createdAt: Scalars['DateTime']['output']; customerPortalUrl?: Maybe; /** Selected fallback when `logo` not set */ @@ -4092,12 +4090,6 @@ export type WorkspaceTeamArgs = { limit?: Scalars['Int']['input']; }; -export type WorkspaceBilling = { - __typename?: 'WorkspaceBilling'; - cost: WorkspaceCost; - versionsCount: WorkspaceVersionsCount; -}; - export type WorkspaceBillingMutations = { __typename?: 'WorkspaceBillingMutations'; cancelCheckoutSession: Scalars['Boolean']['output']; @@ -4792,7 +4784,6 @@ export type ResolversTypes = { WebhookEventCollection: ResolverTypeWrapper; WebhookUpdateInput: WebhookUpdateInput; Workspace: ResolverTypeWrapper; - WorkspaceBilling: ResolverTypeWrapper; WorkspaceBillingMutations: ResolverTypeWrapper; WorkspaceCollaborator: ResolverTypeWrapper; WorkspaceCollaboratorCollection: ResolverTypeWrapper & { items: Array }>; @@ -5053,7 +5044,6 @@ export type ResolversParentTypes = { WebhookEventCollection: WebhookEventCollection; WebhookUpdateInput: WebhookUpdateInput; Workspace: WorkspaceGraphQLReturn; - WorkspaceBilling: WorkspaceBillingGraphQLReturn; WorkspaceBillingMutations: WorkspaceBillingMutationsGraphQLReturn; WorkspaceCollaborator: WorkspaceCollaboratorGraphQLReturn; WorkspaceCollaboratorCollection: Omit & { items: Array }; @@ -6467,7 +6457,6 @@ export type WebhookEventCollectionResolvers = { availableRegions?: Resolver, ParentType, ContextType>; - billing?: Resolver, ParentType, ContextType>; createdAt?: Resolver; customerPortalUrl?: Resolver, ParentType, ContextType>; defaultLogoIndex?: Resolver; @@ -6493,12 +6482,6 @@ export type WorkspaceResolvers; }; -export type WorkspaceBillingResolvers = { - cost?: Resolver; - versionsCount?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type WorkspaceBillingMutationsResolvers = { cancelCheckoutSession?: Resolver>; createCheckoutSession?: Resolver>; @@ -6763,7 +6746,6 @@ export type Resolvers = { WebhookEvent?: WebhookEventResolvers; WebhookEventCollection?: WebhookEventCollectionResolvers; Workspace?: WorkspaceResolvers; - WorkspaceBilling?: WorkspaceBillingResolvers; WorkspaceBillingMutations?: WorkspaceBillingMutationsResolvers; WorkspaceCollaborator?: WorkspaceCollaboratorResolvers; WorkspaceCollaboratorCollection?: WorkspaceCollaboratorCollectionResolvers; 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 babbfdf3e7..e52ed84f4c 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -4010,8 +4010,6 @@ export type Workspace = { __typename?: 'Workspace'; /** Regions available to the workspace for project data residency */ availableRegions: Array; - /** Billing data for Workspaces beta */ - billing?: Maybe; createdAt: Scalars['DateTime']['output']; customerPortalUrl?: Maybe; /** Selected fallback when `logo` not set */ @@ -4073,12 +4071,6 @@ export type WorkspaceTeamArgs = { limit?: Scalars['Int']['input']; }; -export type WorkspaceBilling = { - __typename?: 'WorkspaceBilling'; - cost: WorkspaceCost; - versionsCount: WorkspaceVersionsCount; -}; - export type WorkspaceBillingMutations = { __typename?: 'WorkspaceBillingMutations'; cancelCheckoutSession: Scalars['Boolean']['output']; diff --git a/packages/server/modules/gatekeeper/domain/constants.ts b/packages/server/modules/gatekeeper/domain/constants.ts index a684f0b256..e69de29bb2 100644 --- a/packages/server/modules/gatekeeper/domain/constants.ts +++ b/packages/server/modules/gatekeeper/domain/constants.ts @@ -1 +0,0 @@ -export const WORKSPACE_MAX_PROJECTS_VERSIONS = 500 diff --git a/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts b/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts index f00fb4448c..048395c571 100644 --- a/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts +++ b/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts @@ -30,7 +30,7 @@ import { deleteInviteFactory as deleteInviteFromDbFactory, queryAllUserResourceInvitesFactory, queryAllResourceInvitesFactory, - markInviteUpdatedfactory, + markInviteUpdatedFactory, deleteServerOnlyInvitesFactory, updateAllInviteTargetsFactory } from '@/modules/serverinvites/repositories/serverInvites' @@ -384,7 +384,7 @@ export = { }), findUserByTarget: findUserByTargetFactory({ db }), findInvite: findInviteFactory({ db }), - markInviteUpdated: markInviteUpdatedfactory({ db }), + markInviteUpdated: markInviteUpdatedFactory({ db }), getUser, getServerInfo }) diff --git a/packages/server/modules/serverinvites/repositories/serverInvites.ts b/packages/server/modules/serverinvites/repositories/serverInvites.ts index 97b1080869..bab081b30f 100644 --- a/packages/server/modules/serverinvites/repositories/serverInvites.ts +++ b/packages/server/modules/serverinvites/repositories/serverInvites.ts @@ -501,7 +501,7 @@ export const findInviteByTokenFactory = return (await q) || null } -export const markInviteUpdatedfactory = +export const markInviteUpdatedFactory = ({ db }: { db: Knex }): MarkInviteUpdated => async ({ inviteId }) => { const cols = ServerInvites.with({ withoutTablePrefix: true }).col diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 774e63bc84..a204502135 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -239,10 +239,6 @@ export type EmitWorkspaceEvent = (args: { payload: EventBusPayloads[TEvent] }) => Promise -export type CountProjectsVersionsByWorkspaceId = (args: { - workspaceId: string -}) => Promise - export type CountWorkspaceRoleWithOptionalProjectRole = (args: { workspaceId: string workspaceRole: WorkspaceRoles diff --git a/packages/server/modules/workspaces/errors/workspace.ts b/packages/server/modules/workspaces/errors/workspace.ts index 79ec05de9f..5a81b76b16 100644 --- a/packages/server/modules/workspaces/errors/workspace.ts +++ b/packages/server/modules/workspaces/errors/workspace.ts @@ -113,3 +113,9 @@ export class WorkspaceDomainsInvalidState extends BaseError { static code = 'WORKSPACE_NO_VERIFIED_DOMAINS' static statusCode = 500 } + +export class WorkspacePaidPlanActiveError extends BaseError { + static defaultMessage = 'Workspace has an active paid plan, cancel it first' + static code = 'WORKSPACE_PAID_PLAN_ACTIVE' + static statusCode = 400 +} diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index de0989d01a..f047b296f5 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -24,7 +24,7 @@ import { findInviteFactory, findUserByTargetFactory, insertInviteAndDeleteOldFactory, - markInviteUpdatedfactory, + markInviteUpdatedFactory, queryAllResourceInvitesFactory, queryAllUserResourceInvitesFactory, updateAllInviteTargetsFactory @@ -49,6 +49,7 @@ import { WorkspaceInvalidRoleError, WorkspaceJoinNotAllowedError, WorkspaceNotFoundError, + WorkspacePaidPlanActiveError, WorkspacesNotAuthorizedError, WorkspacesNotYetImplementedError } from '@/modules/workspaces/errors/workspace' @@ -68,11 +69,9 @@ import { getWorkspaceDomainsFactory, getUserDiscoverableWorkspacesFactory, getWorkspaceWithDomainsFactory, - countProjectsVersionsByWorkspaceIdFactory, - countWorkspaceRoleWithOptionalProjectRoleFactory, - getUserIdsWithRoleInWorkspaceFactory, getWorkspaceRoleForUserFactory, - getWorkspaceBySlugFactory + getWorkspaceBySlugFactory, + countDomainsByWorkspaceIdFactory } from '@/modules/workspaces/repositories/workspaces' import { buildWorkspaceInviteEmailContentsFactory, @@ -106,7 +105,12 @@ import { getPaginatedWorkspaceTeamFactory, getWorkspacesForUserFactory } from '@/modules/workspaces/services/retrieval' -import { Roles, WorkspaceRoles, removeNullOrUndefinedKeys } from '@speckle/shared' +import { + Roles, + WorkspaceRoles, + removeNullOrUndefinedKeys, + throwUncoveredError +} from '@speckle/shared' import { chunk } from 'lodash' import { findEmailsByUserIdFactory, @@ -118,11 +122,6 @@ import { import { joinWorkspaceFactory } from '@/modules/workspaces/services/join' import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails' import { requestNewEmailVerificationFactory } from '@/modules/emails/services/verification/request' -import { WORKSPACE_MAX_PROJECTS_VERSIONS } from '@/modules/gatekeeper/domain/constants' -import { - getWorkspaceCostFactory, - getWorkspaceCostItemsFactory -} from '@/modules/workspaces/services/cost' import { deleteWorkspaceDomainFactory, isUserWorkspaceDomainPolicyCompliantFactory @@ -162,7 +161,7 @@ import { } from '@/modules/core/services/ratelimiter' import { RateLimitError } from '@/modules/core/errors/ratelimit' import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' -import { getDb } from '@/modules/multiregion/dbSelector' +import { getDb, getRegionDb } from '@/modules/multiregion/dbSelector' import { createNewProjectFactory } from '@/modules/core/services/projects' import { deleteProjectFactory, @@ -184,6 +183,8 @@ import { import { getDecryptor } from '@/modules/workspaces/helpers/sso' import { getDefaultRegionFactory } from '@/modules/workspaces/repositories/regions' import { storeModelFactory } from '@/modules/core/repositories/models' +import { getWorkspacePlanFactory } from '@/modules/gatekeeper/repositories/billing' +import { Knex } from 'knex' const eventBus = getEventBus() const getServerInfo = getServerInfoFactory({ db }) @@ -241,7 +242,6 @@ const buildCreateAndSendWorkspaceInvite = () => getUser, getServerInfo }) -const deleteStream = deleteStreamFactory({ db }) const saveActivity = saveActivityFactory({ db }) const validateStreamAccess = validateStreamAccessFactory({ authorizeResolver }) const isStreamCollaborator = isStreamCollaboratorFactory({ @@ -448,18 +448,51 @@ export = FF_WORKSPACES_MODULE_ENABLED context.resourceAccessRules ) - // Delete workspace and associated resources (i.e. invites) - const deleteWorkspace = deleteWorkspaceFactory({ - deleteWorkspace: repoDeleteWorkspaceFactory({ db }), - deleteProject: deleteStream, - deleteAllResourceInvites: deleteAllResourceInvitesFactory({ db }), - queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({ - getStreams: legacyGetStreamsFactory({ db }) - }), - deleteSsoProvider: deleteSsoProviderFactory({ db }) - }) + const workspacePlan = await getWorkspacePlanFactory({ db })({ workspaceId }) + if (workspacePlan) { + switch (workspacePlan.name) { + case 'team': + case 'pro': + case 'business': + switch (workspacePlan.status) { + case 'cancelationScheduled': + case 'valid': + case 'paymentFailed': + throw new WorkspacePaidPlanActiveError() + case 'canceled': + case 'trial': + case 'expired': + break + default: + throwUncoveredError(workspacePlan) + } + case 'unlimited': + case 'academia': + break + default: + throwUncoveredError(workspacePlan) + } + } + + const deleteWorkspaceFrom = (db: Knex) => + deleteWorkspaceFactory({ + deleteWorkspace: repoDeleteWorkspaceFactory({ db }), + deleteProject: deleteStreamFactory({ db }), + deleteAllResourceInvites: deleteAllResourceInvitesFactory({ db }), + queryAllWorkspaceProjects: queryAllWorkspaceProjectsFactory({ + getStreams: legacyGetStreamsFactory({ db }) + }), + deleteSsoProvider: deleteSsoProviderFactory({ db }) + }) - await deleteWorkspace({ workspaceId }) + // this should be turned into a get all regions and map over the regions... + const region = await getDefaultRegionFactory({ db })({ workspaceId }) + if (region) { + const regionDb = await getRegionDb({ regionKey: region.key }) + await deleteWorkspaceFrom(regionDb)({ workspaceId }) + } + + await deleteWorkspaceFrom(db)({ workspaceId }) return true }, @@ -570,7 +603,7 @@ export = FF_WORKSPACES_MODULE_ENABLED ) await deleteWorkspaceDomainFactory({ deleteWorkspaceDomain: repoDeleteWorkspaceDomainFactory({ db }), - countDomainsByWorkspaceId: countProjectsVersionsByWorkspaceIdFactory({ + countDomainsByWorkspaceId: countDomainsByWorkspaceIdFactory({ db }), updateWorkspace: updateWorkspaceFactory({ @@ -656,7 +689,7 @@ export = FF_WORKSPACES_MODULE_ENABLED db, filterQuery: workspaceInviteValidityFilter }), - markInviteUpdated: markInviteUpdatedfactory({ db }), + markInviteUpdated: markInviteUpdatedFactory({ db }), getUser, getServerInfo }) @@ -956,35 +989,12 @@ export = FF_WORKSPACES_MODULE_ENABLED domains: async (parent) => { return await getWorkspaceDomainsFactory({ db })({ workspaceIds: [parent.id] }) }, - billing: (parent) => ({ parent }), sso: async (parent) => { return await getWorkspaceSsoProviderRecordFactory({ db })({ workspaceId: parent.id }) } }, - WorkspaceBilling: { - versionsCount: async ({ parent }) => { - const workspaceId = parent.id - return { - current: await countProjectsVersionsByWorkspaceIdFactory({ db })({ - workspaceId - }), - max: WORKSPACE_MAX_PROJECTS_VERSIONS - } - }, - cost: async ({ parent }) => { - const workspaceId = parent.id - return getWorkspaceCostFactory({ - getWorkspaceCostItems: getWorkspaceCostItemsFactory({ - countRole: countWorkspaceRoleWithOptionalProjectRoleFactory({ db }), - getUserIdsWithRoleInWorkspace: getUserIdsWithRoleInWorkspaceFactory({ - db - }) - }) - })({ workspaceId }) - } - }, WorkspaceSso: { provider: async ({ workspaceId }) => { const provider = await getWorkspaceSsoProviderFactory({ diff --git a/packages/server/modules/workspaces/repositories/workspaces.ts b/packages/server/modules/workspaces/repositories/workspaces.ts index 4504017877..ea89fe0a9e 100644 --- a/packages/server/modules/workspaces/repositories/workspaces.ts +++ b/packages/server/modules/workspaces/repositories/workspaces.ts @@ -6,7 +6,6 @@ import { } from '@/modules/workspacesCore/domain/types' import { CountDomainsByWorkspaceId, - CountProjectsVersionsByWorkspaceId, CountWorkspaceRoleWithOptionalProjectRole, DeleteWorkspace, DeleteWorkspaceDomain, @@ -42,7 +41,6 @@ import { ServerAcl, ServerInvites, StreamAcl, - StreamCommits, Streams, Users } from '@/modules/core/dbSchema' @@ -388,18 +386,6 @@ export const getWorkspaceWithDomainsFactory = } as Workspace & { domains: WorkspaceDomain[] } } -export const countProjectsVersionsByWorkspaceIdFactory = - ({ db }: { db: Knex }): CountProjectsVersionsByWorkspaceId => - async ({ workspaceId }) => { - const [res] = await tables - .streams(db) - .join(StreamCommits.name, StreamCommits.col.streamId, Streams.col.id) - .where({ workspaceId }) - .count(StreamCommits.col.commitId) - - return parseInt(res.count.toString()) - } - export const getUserIdsWithRoleInWorkspaceFactory = ({ db }: { db: Knex }): GetUserIdsWithRoleInWorkspace => async ({ workspaceId, workspaceRole }, options) => { diff --git a/packages/server/modules/workspaces/services/cost.ts b/packages/server/modules/workspaces/services/cost.ts deleted file mode 100644 index 3f2d9a5fa7..0000000000 --- a/packages/server/modules/workspaces/services/cost.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { - CountWorkspaceRoleWithOptionalProjectRole, - GetUserIdsWithRoleInWorkspace -} from '@/modules/workspaces/domain/operations' -import { Roles, throwUncoveredError } from '@speckle/shared' - -type KnownWorkspaceCostItemNames = - | 'workspace-members' - | 'free-guests' - | 'read-write-guests' - | 'read-only-guests' - -type KnownCurrencies = 'GBP' - -type WorkspaceCostItem = { - name: KnownWorkspaceCostItemNames - description: string - count: number - cost: number - label: string -} - -const getWorkspaceCostItemCost = ({ - name -}: { - name: KnownWorkspaceCostItemNames - currency?: KnownCurrencies -}): number => { - switch (name) { - case 'workspace-members': - return 49 - case 'free-guests': - return 0 - case 'read-write-guests': - return 15 - case 'read-only-guests': - return 5 - default: - throwUncoveredError(name) - } -} - -type GetWorkspaceCostItems = (args: { - workspaceId: string -}) => Promise - -export const getWorkspaceCostItemsFactory = - ({ - countRole, - getUserIdsWithRoleInWorkspace - }: { - countRole: CountWorkspaceRoleWithOptionalProjectRole - getUserIdsWithRoleInWorkspace: GetUserIdsWithRoleInWorkspace - }): GetWorkspaceCostItems => - async ({ workspaceId }) => { - const freeGuestsIds = await getUserIdsWithRoleInWorkspace( - { - workspaceId, - workspaceRole: Roles.Workspace.Guest - }, - { limit: 10 } - ) - const [adminCount, memberCount, writeGuestCount, readGuestCount] = - await Promise.all([ - countRole({ workspaceId, workspaceRole: Roles.Workspace.Admin }), - countRole({ workspaceId, workspaceRole: Roles.Workspace.Member }), - countRole({ - workspaceId, - workspaceRole: Roles.Workspace.Guest, - projectRole: Roles.Stream.Contributor, - skipUserIds: freeGuestsIds - }), - countRole({ - workspaceId, - workspaceRole: Roles.Workspace.Guest, - projectRole: Roles.Stream.Reviewer, - skipUserIds: freeGuestsIds - }) - ]) - - const workspaceCostItems: WorkspaceCostItem[] = [] - - const workspaceMembersCount = adminCount + memberCount - const freeGuestsCount = freeGuestsIds.length - - workspaceCostItems.push({ - name: 'workspace-members', - description: 'General workspace member', - count: workspaceMembersCount, - cost: getWorkspaceCostItemCost({ name: 'workspace-members' }), - label: `${workspaceMembersCount} workspace ${ - workspaceMembersCount === 1 ? 'member' : 'members' - }` - }) - workspaceCostItems.push({ - name: 'free-guests', - description: 'The first 10 workspace guests are free', - count: freeGuestsCount, - cost: getWorkspaceCostItemCost({ name: 'free-guests' }), - label: `${freeGuestsCount}/10 free ${freeGuestsCount === 1 ? 'guest' : 'guests'}` - }) - workspaceCostItems.push({ - name: 'read-write-guests', - description: 'Workspace guests with write access to minimum 1 workspace project', - count: writeGuestCount, - cost: getWorkspaceCostItemCost({ name: 'read-write-guests' }), - label: `${writeGuestCount} read/write ${ - writeGuestCount === 1 ? 'guest' : 'guests' - }` - }) - workspaceCostItems.push({ - name: 'read-only-guests', - description: 'Workspace guests with only read access to some workspace projects', - count: readGuestCount, - cost: getWorkspaceCostItemCost({ name: 'read-only-guests' }), - label: `${readGuestCount} read only ${readGuestCount === 1 ? 'guest' : 'guests'}` - }) - - return workspaceCostItems - } - -type WorkspaceDiscount = { - name: string - amount: number -} - -type WorkspaceCost = { - subTotal: number - currency: KnownCurrencies - items: WorkspaceCostItem[] - total: number - discount?: WorkspaceDiscount -} - -export const calculateWorkspaceTotalCost = ({ - subTotal, - discount -}: Pick) => { - if (!discount) { - return subTotal - } - return subTotal * discount?.amount -} - -export const getWorkspaceCostFactory = - ({ - getWorkspaceCostItems, - discount - }: { - getWorkspaceCostItems: GetWorkspaceCostItems - discount?: WorkspaceDiscount - }) => - async ({ workspaceId }: { workspaceId: string }): Promise => { - const items = await getWorkspaceCostItems({ workspaceId }) - - const subTotal = items.reduce((acc, { cost, count }) => acc + cost * count, 0) - - return { - currency: 'GBP', - items, - subTotal, - discount, - total: calculateWorkspaceTotalCost({ - subTotal, - discount - }) - } - } diff --git a/packages/server/modules/workspaces/tests/helpers/graphql.ts b/packages/server/modules/workspaces/tests/helpers/graphql.ts index ee22629317..6af5589fcd 100644 --- a/packages/server/modules/workspaces/tests/helpers/graphql.ts +++ b/packages/server/modules/workspaces/tests/helpers/graphql.ts @@ -31,31 +31,6 @@ export const basicPendingWorkspaceCollaboratorFragment = gql` } ` -export const workspaceBillingFragment = gql` - fragment WorkspaceBilling on Workspace { - billing { - versionsCount { - current - max - } - cost { - subTotal - currency - items { - count - name - cost - label - } - discount { - name - amount - } - total - } - } - } -` export const workspaceProjectsFragment = gql` fragment WorkspaceProjects on ProjectCollection { items { @@ -122,17 +97,6 @@ export const getWorkspaceWithTeamQuery = gql` ${basicPendingWorkspaceCollaboratorFragment} ` -export const getWorkspaceWithBillingQuery = gql` - query GetWorkspaceWithBilling($workspaceId: String!) { - workspace(id: $workspaceId) { - ...BasicWorkspace - ...WorkspaceBilling - } - } - ${basicWorkspaceFragment} - ${workspaceBillingFragment} -` - export const getWorkspaceWithProjectsQuery = gql` query GetWorkspaceWithProjects($workspaceId: String!) { workspace(id: $workspaceId) { 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 f12ee00045..928ffb1786 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts @@ -23,9 +23,6 @@ import { UpdateWorkspaceDocument, UpdateWorkspaceRoleDocument, ActiveUserLeaveWorkspaceDocument, - GetWorkspaceWithBillingDocument, - CreateObjectDocument, - CreateProjectVersionDocument, GetWorkspaceWithProjectsDocument, AddWorkspaceDomainDocument, DeleteWorkspaceDomainDocument, @@ -50,62 +47,9 @@ import { } from '@/modules/core/helpers/testHelpers' import { getWorkspaceFactory } from '@/modules/workspaces/repositories/workspaces' import { grantStreamPermissionsFactory } from '@/modules/core/repositories/streams' -import { getPaginatedStreamBranchesFactory } from '@/modules/core/services/branch/retrieval' -import { - getPaginatedStreamBranchesPageFactory, - getStreamBranchCountFactory -} from '@/modules/core/repositories/branches' -const getBranchesByStreamId = getPaginatedStreamBranchesFactory({ - getPaginatedStreamBranchesPage: getPaginatedStreamBranchesPageFactory({ db }), - getStreamBranchCount: getStreamBranchCountFactory({ db }) -}) const grantStreamPermissions = grantStreamPermissionsFactory({ db }) -const createProjectWithVersions = - ({ apollo }: { apollo: TestApolloServer }) => - async ({ - workspaceId, - versionsCount - }: { - workspaceId: string - versionsCount: number - }) => { - const resProject1 = await apollo.execute(CreateWorkspaceProjectDocument, { - input: { - name: createRandomPassword(), - workspaceId - } - }) - expect(resProject1).to.not.haveGraphQLErrors() - const project1Id = resProject1.data!.workspaceMutations.projects.create.id - - const { - items: [model1] - } = await getBranchesByStreamId(project1Id, { limit: 1, cursor: null }) - expect(model1).to.exist - - const resObj1 = await apollo.execute(CreateObjectDocument, { - input: { - streamId: project1Id, - objects: [{ some: 'obj' }] - } - }) - expect(resObj1).to.not.haveGraphQLErrors() - - await Promise.all( - new Array(versionsCount).fill(0).map(async () => { - const res = await apollo.execute(CreateProjectVersionDocument, { - input: { - projectId: project1Id, - modelId: model1.id, - objectId: resObj1.data!.objectCreate[0] - } - }) - expect(res).to.not.haveGraphQLErrors() - }) - ) - } describe('Workspaces GQL CRUD', () => { let apollo: TestApolloServer @@ -544,159 +488,53 @@ describe('Workspaces GQL CRUD', () => { }) }) - describe('query workspace.billing', () => { - it('should return workspace version limits', async () => { - await createProjectWithVersions({ apollo })({ - workspaceId: workspace.id, - versionsCount: 3 - }) - await createProjectWithVersions({ apollo })({ - workspaceId: workspace.id, - versionsCount: 2 - }) - - const res = await apollo.execute(GetWorkspaceWithBillingDocument, { - workspaceId: workspace.id - }) - - expect(res).to.not.haveGraphQLErrors() - expect(res.data?.workspace.billing?.versionsCount).to.deep.equal({ - current: 5, - max: 500 - }) - }) + describe('query activeUser.workspaces', () => { + it('should return all workspaces for a user', async () => { + const testUser: BasicTestUser = { + id: '', + name: 'John Speckle', + email: 'foobar@example.org', + role: Roles.Server.Admin, + verified: true + } - it('should return workspace cost', async () => { - const createRes = await apollo.execute(CreateWorkspaceDocument, { - input: { - name: createRandomString(), - slug: cryptoRandomString({ length: 10 }) - } + await createTestUser(testUser) + const testApollo: TestApolloServer = await testApolloServer({ + context: await createTestContext({ + auth: true, + userId: testUser.id, + token: '', + role: testUser.role, + scopes: AllScopes + }) }) - expect(createRes).to.not.haveGraphQLErrors() - const workspaceId = createRes.data!.workspaceMutations.create.id - const workspace = (await getWorkspaceFactory({ db })({ - workspaceId - })) as unknown as BasicTestWorkspace - const member = { - id: createRandomString(), - name: createRandomPassword(), - email: createRandomEmail() - } - const freeGuests = new Array(10).fill(0).map(() => ({ - id: createRandomString(), - name: createRandomPassword(), - email: createRandomEmail() - })) - const guestWithWritePermission = { - id: createRandomString(), - name: createRandomPassword(), - email: createRandomEmail() - } - const viewer = { - id: createRandomString(), - name: createRandomPassword(), - email: createRandomEmail() - } - const viewer2 = { - id: createRandomString(), - name: createRandomPassword(), - email: createRandomEmail() + const workspace1: BasicTestWorkspace = { + id: '', + ownerId: '', + name: 'Workspace A', + slug: cryptoRandomString({ length: 10 }) } - // first 10 users - await createTestUsers(freeGuests) - for (const guest of freeGuests) { - await assignToWorkspace(workspace, guest, Roles.Workspace.Guest) + const workspace2: BasicTestWorkspace = { + id: '', + ownerId: '', + name: 'Workspace A', + slug: cryptoRandomString({ length: 10 }) } - await Promise.all([ - createTestUser(member), - createTestUser(guestWithWritePermission), - createTestUser(viewer), - createTestUser(viewer2) - ]) - - await assignToWorkspace(workspace, member, Roles.Workspace.Member) - await assignToWorkspace( - workspace, - guestWithWritePermission, - Roles.Workspace.Guest - ) - await assignToWorkspace(workspace, viewer, Roles.Workspace.Guest) - await assignToWorkspace(workspace, viewer2, Roles.Workspace.Guest) - - const resProject1 = await apollo.execute(CreateWorkspaceProjectDocument, { - input: { - name: createRandomPassword(), - workspaceId - } - }) - expect(resProject1).to.not.haveGraphQLErrors() - const project1Id = resProject1.data!.workspaceMutations.projects.create.id - - await Promise.all([ - grantStreamPermissions({ - streamId: project1Id, - userId: guestWithWritePermission.id, - role: Roles.Stream.Contributor - }), - grantStreamPermissions({ - streamId: project1Id, - userId: viewer.id, - role: Roles.Stream.Reviewer - }), - grantStreamPermissions({ - streamId: project1Id, - userId: viewer2.id, - role: Roles.Stream.Reviewer - }) - ]) + const workspace3: BasicTestWorkspace = { + id: '', + ownerId: '', + name: 'Workspace A', + slug: cryptoRandomString({ length: 10 }) + } - const res = await apollo.execute(GetWorkspaceWithBillingDocument, { - workspaceId - }) + await createTestWorkspace(workspace1, testUser) + await createTestWorkspace(workspace2, testUser) + await createTestWorkspace(workspace3, testUser) - expect(res).to.not.haveGraphQLErrors() - const { subTotal, currency, items, total, discount } = - res.data?.workspace.billing?.cost || {} - expect(subTotal).to.equal(49 + 49 + 15 + 2 * 5) - expect(currency).to.equal('GBP') - expect(items).to.deep.equal([ - { - name: 'workspace-members', - count: 2, - cost: 49, - label: '2 workspace members' - }, - { - name: 'free-guests', - count: 10, - cost: 0, - label: '10/10 free guests' - }, - { - name: 'read-write-guests', - count: 1, - cost: 15, - label: '1 read/write guest' - }, - { - name: 'read-only-guests', - count: 2, - cost: 5, - label: '2 read only guests' - } - ]) - expect(discount).to.deep.equal(null) - expect(total).to.equal(123) - }) - }) - - describe('query activeUser.workspaces', () => { - it('should return all workspaces for a user', async () => { - const res = await apollo.execute(GetActiveUserWorkspacesDocument, {}) + const res = await testApollo.execute(GetActiveUserWorkspacesDocument, {}) expect(res).to.not.haveGraphQLErrors() // TODO: this test depends on the previous tests expect(res.data?.activeUser?.workspaces?.items?.length).to.equal(3) @@ -786,7 +624,7 @@ describe('Workspaces GQL CRUD', () => { } before(async () => { - await createTestWorkspace(workspace, testAdminUser) + await createTestWorkspace(workspace, testAdminUser, { addPlan: false }) workspaceProject.workspaceId = workspace.id @@ -820,6 +658,22 @@ describe('Workspaces GQL CRUD', () => { }) }) + it('should fail to delete a paid workspace', async () => { + const paidWorkspace = { + id: '', + name: 'test ws', + slug: cryptoRandomString({ length: 10 }), + ownerId: '' + } + + await createTestWorkspace(paidWorkspace, testAdminUser, { addPlan: true }) + const deleteRes = await apollo.execute(DeleteWorkspaceDocument, { + workspaceId: paidWorkspace.id + }) + + expect(deleteRes).to.haveGraphQLErrors('Workspace has an active paid plan') + }) + it('should delete the workspace', async () => { const deleteRes = await apollo.execute(DeleteWorkspaceDocument, { workspaceId: workspace.id @@ -833,18 +687,25 @@ describe('Workspaces GQL CRUD', () => { }) it('should throw if non-workspace-admin triggers delete', async () => { - const memberApollo: TestApolloServer = (apollo = await testApolloServer({ + const nonPaidWorkspace = { + id: '', + name: 'test ws', + slug: cryptoRandomString({ length: 10 }), + ownerId: '' + } + await createTestWorkspace(nonPaidWorkspace, testAdminUser, { addPlan: false }) + const memberApollo: TestApolloServer = await testApolloServer({ context: await createTestContext({ auth: true, - userId: testAdminUser.id, + userId: testMemberUser.id, token: '', - role: testAdminUser.role, + role: testMemberUser.role, scopes: AllScopes }) - })) + }) const res = await memberApollo.execute(DeleteWorkspaceDocument, { - workspaceId: workspace.id + workspaceId: nonPaidWorkspace.id }) expect(res).to.haveGraphQLErrors('not authorized') diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index a14fd3f550..e22e6d8680 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -4011,8 +4011,6 @@ export type Workspace = { __typename?: 'Workspace'; /** Regions available to the workspace for project data residency */ availableRegions: Array; - /** Billing data for Workspaces beta */ - billing?: Maybe; createdAt: Scalars['DateTime']['output']; customerPortalUrl?: Maybe; /** Selected fallback when `logo` not set */ @@ -4074,12 +4072,6 @@ export type WorkspaceTeamArgs = { limit?: Scalars['Int']['input']; }; -export type WorkspaceBilling = { - __typename?: 'WorkspaceBilling'; - cost: WorkspaceCost; - versionsCount: WorkspaceVersionsCount; -}; - export type WorkspaceBillingMutations = { __typename?: 'WorkspaceBillingMutations'; cancelCheckoutSession: Scalars['Boolean']['output']; @@ -4467,8 +4459,6 @@ export type BasicWorkspaceFragment = { __typename?: 'Workspace', id: string, nam 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 }; -export type WorkspaceBillingFragment = { __typename?: 'Workspace', 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 WorkspaceProjectsFragment = { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string }> }; export type CreateWorkspaceInviteMutationVariables = Exact<{ @@ -4494,13 +4484,6 @@ export type GetWorkspaceWithTeamQueryVariables = Exact<{ 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, 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']; }>; @@ -5246,7 +5229,6 @@ export type MoveProjectToWorkspaceMutation = { __typename?: 'Mutation', workspac 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; export const BasicStreamAccessRequestFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"StreamAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; export const TestAutomateFunctionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestAutomateFunction"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"repo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"owner"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isFeatured"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"releases"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"5"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versionTag"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"inputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"commitId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"automationCount"}},{"kind":"Field","name":{"kind":"Name","value":"supportedSourceApps"}},{"kind":"Field","name":{"kind":"Name","value":"tags"}}]}}]} as unknown as DocumentNode; @@ -5268,7 +5250,6 @@ export const CreateObjectDocument = {"kind":"Document","definitions":[{"kind":"O 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;