Files
speckle-server/packages/server/modules/workspaces/graph/resolvers/workspaces.ts
T
Chuck Driesler 6eaf3c8c92 feat(workspaces): cru(d) resolvers (#2521)
* feat(workspaces): drop createdByUserId from the dataschema

* feat(workspaces): repositories WIP

* merge

* protect against removing last admin in workspace

* quick impl and stub tests

* add tests

* services

* unit tests for role services

* feat(workspaces): authorize project creation if workspace specified

* feat(workspaces): emit project created event

* feat(workspaces): assign roles on project create in workspace

* feat(workspaces): update project roles when user added to workspace

* feat(workspaces): stencil gql resolvers

* fix(workspaces): lol lmao

* fix(workspaces): perform automatic project role update in service function

* fix(workspaces): also delete roles

* fix(workspaces): broke tests again oops

* fix(workspaces): update `onProjectCreated` listener to use new repo method

* fix(workspaces): use service function in event listener

* fix(workspaces): get workspace projects via existing stream repo functions

* fix(workspaces): roles mapping in domain, use enum

* feat(workspaces): stencil gql api and resolvers

* fix(workspaces): repair type reference in tests

* fix(workspaces): consolidate files, use different existing stream-getter

* fix(workspaces): more specific error

* fix(workspaces): roles and scopes

* fix(workspaces): yield per page

* fix(workspaces): some test dry

* fix(workspaces): superdry

* fix(workspaces): add scopes

* fix(workspaces): classic

* feat(workspaces): create workspace mutation

* feat(workspaces): I'm sure everything will be fine

* fix(workspaces): yep

* fix(workspaces): successful gql e2e test

* feat(workspaces): update workspace resolver

* chore(workspaces): update resolver test

* feat(workspaces): some retrieval resolvers

* chore(workspaces): tests for query resolvers

* fix(chore): revert temp test command change

* fix(workspaces): test structure and gql types

* fix(workspaces): validate user authz to perform some operations

* fix(workspaces): use existing test infrastructure

* fix(workspaces): stop `isPublic` check if authorizing a workspace resource

* fix(workspaces): better test hygiene

---------

Co-authored-by: Gergő Jedlicska <gergo@jedlicska.com>
2024-07-25 12:58:28 +01:00

175 lines
5.2 KiB
TypeScript

import { Resolvers } from '@/modules/core/graph/generated/graphql'
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
import {
WorkspaceNotFoundError,
WorkspacesNotAuthorizedError,
WorkspacesNotYetImplementedError
} from '@/modules/workspaces/errors/workspace'
import {
getWorkspaceFactory,
getWorkspaceRolesForUserFactory,
upsertWorkspaceFactory,
upsertWorkspaceRoleFactory
} from '@/modules/workspaces/repositories/workspaces'
import {
createWorkspaceFactory,
updateWorkspaceFactory
} from '@/modules/workspaces/services/management'
import db from '@/db/knex'
import { getEventBus } from '@/modules/shared/services/eventBus'
import { getWorkspacesForUserFactory } from '@/modules/workspaces/services/retrieval'
const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags()
export = FF_WORKSPACES_MODULE_ENABLED
? ({
Query: {
workspace: async (_parent, args) => {
const { id: workspaceId } = args
// TODO: Use dataloader
const workspace = await getWorkspaceFactory({ db })({ workspaceId })
if (!workspace) {
throw new WorkspaceNotFoundError()
}
return workspace
}
},
Mutation: {
workspaceMutations: () => ({})
},
WorkspaceMutations: {
create: async (_parent, args, context) => {
const { name, description, logoUrl } = args.input
const { emit: emitWorkspaceEvent } = getEventBus()
const upsertWorkspace = upsertWorkspaceFactory({ db })
const upsertWorkspaceRole = upsertWorkspaceRoleFactory({ db })
// TODO: Integrate with blobstorage
const storeBlob = async () => ''
const createWorkspace = createWorkspaceFactory({
upsertWorkspace,
upsertWorkspaceRole,
emitWorkspaceEvent,
storeBlob
})
const workspace = await createWorkspace({
userId: context.userId!,
workspaceInput: {
name,
description: description ?? null,
logoUrl: logoUrl ?? null
}
})
return workspace
},
delete: async () => {
throw new WorkspacesNotYetImplementedError()
},
update: async (_parent, args, context) => {
const { id: workspaceId, ...workspaceInput } = args.input
const { emit: emitWorkspaceEvent } = getEventBus()
const getWorkspace = getWorkspaceFactory({ db })
const upsertWorkspace = upsertWorkspaceFactory({ db })
// TODO: Integrate with blobstorage
const storeBlob = async () => ''
const updateWorkspace = updateWorkspaceFactory({
getWorkspace,
upsertWorkspace,
emitWorkspaceEvent,
storeBlob
})
const workspace = await updateWorkspace({
workspaceId,
workspaceInput,
workspaceUpdaterId: context.userId!
})
return workspace
},
updateRole: async () => {
throw new WorkspacesNotYetImplementedError()
},
deleteRole: async () => {
throw new WorkspacesNotYetImplementedError()
}
},
WorkspaceInviteMutations: {
create: async () => {
throw new WorkspacesNotYetImplementedError()
},
batchCreate: async () => {
throw new WorkspacesNotYetImplementedError()
},
use: async () => {
throw new WorkspacesNotYetImplementedError()
},
cancel: async () => {
throw new WorkspacesNotYetImplementedError()
}
},
Workspace: {
role: async () => {
// Get user id from parent, get role and return
throw new WorkspacesNotYetImplementedError()
},
team: async () => {
// Get roles for workspace
throw new WorkspacesNotYetImplementedError()
},
invitedTeam: async () => {
// Get invites
throw new WorkspacesNotYetImplementedError()
},
projects: async () => {
// Get projects in workspace
throw new WorkspacesNotYetImplementedError()
}
},
User: {
workspaces: async (_parent, _args, context) => {
if (!context.userId) {
throw new WorkspacesNotAuthorizedError()
}
const getWorkspace = getWorkspaceFactory({ db })
const getWorkspaceRolesForUser = getWorkspaceRolesForUserFactory({ db })
const getWorkspacesForUser = getWorkspacesForUserFactory({
getWorkspace,
getWorkspaceRolesForUser
})
const workspaces = await getWorkspacesForUser({ userId: context.userId })
// TODO: Pagination
return {
items: workspaces,
totalCount: workspaces.length
}
}
},
Project: {
workspace: async () => {
// Get workspaceId from project, get and return workspace data
throw new WorkspacesNotYetImplementedError()
}
},
AdminQueries: {
workspaceList: async () => {
throw new WorkspacesNotYetImplementedError()
}
}
} as Resolvers)
: {}