Merge branch 'main' of github.com:specklesystems/speckle-server into alessandro/web-1171-change-the-users-repository-to-update-the-verified-field-in
This commit is contained in:
Коммит
295b6d0621
|
@ -41,7 +41,7 @@ repos:
|
|||
- id: helm-documentation
|
||||
name: Helm Json Schema
|
||||
language: system
|
||||
files: utils/helm/speckle-server/values.yaml
|
||||
files: utils\/helm\/speckle\-server\/values\.yaml
|
||||
entry: utils/helm/update-schema-json.sh
|
||||
description: If this fails it is because the values.yaml file was updated. Or has missing or incorrect documentation.
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ const deleteConfirmed = async () => {
|
|||
},
|
||||
{
|
||||
update: (cache, { data }) => {
|
||||
if (data?.streamsDelete) {
|
||||
if (data?.projectMutations.batchDelete) {
|
||||
// Remove project from cache
|
||||
const cacheId = getCacheId('Project', projectId)
|
||||
cache.evict({
|
||||
|
@ -103,7 +103,7 @@ const deleteConfirmed = async () => {
|
|||
}
|
||||
).catch(convertThrowIntoFetchResult)
|
||||
|
||||
if (result?.data?.streamsDelete) {
|
||||
if (result?.data?.projectMutations.batchDelete) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Success,
|
||||
title: 'Project deleted',
|
||||
|
|
|
@ -275,6 +275,16 @@ import { useFunctionRunsStatusSummary } from '~/lib/automate/composables/runStat
|
|||
|
||||
const isGendoEnabled = useIsGendoModuleEnabled()
|
||||
|
||||
enum ViewerKeyboardActions {
|
||||
ToggleModels = 'ToggleModels',
|
||||
ToggleExplorer = 'ToggleExplorer',
|
||||
ToggleDiscussions = 'ToggleDiscussions',
|
||||
ToggleMeasurements = 'ToggleMeasurements',
|
||||
ToggleProjection = 'ToggleProjection',
|
||||
ToggleSectionBox = 'ToggleSectionBox',
|
||||
ZoomExtentsOrSelection = 'ZoomExtentsOrSelection'
|
||||
}
|
||||
|
||||
const width = ref(360)
|
||||
const scrollableControlsContainer = ref(null as Nullable<HTMLDivElement>)
|
||||
|
||||
|
@ -364,28 +374,72 @@ const {
|
|||
diff: { enabled }
|
||||
} = useInjectedViewerInterfaceState()
|
||||
|
||||
const map: Record<ViewerKeyboardActions, [ModifierKeys[], string]> = {
|
||||
[ViewerKeyboardActions.ToggleModels]: [[ModifierKeys.Shift], 'm'],
|
||||
[ViewerKeyboardActions.ToggleExplorer]: [[ModifierKeys.Shift], 'e'],
|
||||
[ViewerKeyboardActions.ToggleDiscussions]: [[ModifierKeys.Shift], 't'],
|
||||
[ViewerKeyboardActions.ToggleMeasurements]: [[ModifierKeys.Shift], 'r'],
|
||||
[ViewerKeyboardActions.ToggleProjection]: [[ModifierKeys.Shift], 'p'],
|
||||
[ViewerKeyboardActions.ToggleSectionBox]: [[ModifierKeys.Shift], 'b'],
|
||||
[ViewerKeyboardActions.ZoomExtentsOrSelection]: [[ModifierKeys.Shift], 'space']
|
||||
}
|
||||
|
||||
const getShortcutTitle = (action: ViewerKeyboardActions) =>
|
||||
`(${getKeyboardShortcutTitle([...map[action][0], map[action][1]])})`
|
||||
|
||||
const modelsShortcut = ref(
|
||||
`Models (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'm'])})`
|
||||
`Models ${getShortcutTitle(ViewerKeyboardActions.ToggleModels)}`
|
||||
)
|
||||
const explorerShortcut = ref(
|
||||
`Scene Explorer (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'e'])})`
|
||||
`Scene Explorer ${getShortcutTitle(ViewerKeyboardActions.ToggleExplorer)}`
|
||||
)
|
||||
const discussionsShortcut = ref(
|
||||
`Discussions (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 't'])})`
|
||||
`Discussions ${getShortcutTitle(ViewerKeyboardActions.ToggleDiscussions)}`
|
||||
)
|
||||
const zoomExtentsShortcut = ref(
|
||||
`Fit to screen (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'Space'])})`
|
||||
`Fit to screen ${getShortcutTitle(ViewerKeyboardActions.ZoomExtentsOrSelection)}`
|
||||
)
|
||||
const projectionShortcut = ref(
|
||||
`Projection (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'p'])})`
|
||||
`Projection ${getShortcutTitle(ViewerKeyboardActions.ToggleProjection)}`
|
||||
)
|
||||
const sectionBoxShortcut = ref(
|
||||
`Section Box (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'b'])})`
|
||||
`Section Box ${getShortcutTitle(ViewerKeyboardActions.ToggleSectionBox)}`
|
||||
)
|
||||
const measureShortcut = ref(
|
||||
`Measure Mode (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'd'])})`
|
||||
`Measure Mode ${getShortcutTitle(ViewerKeyboardActions.ToggleMeasurements)}`
|
||||
)
|
||||
|
||||
const handleKeyboardAction = (action: ViewerKeyboardActions) => {
|
||||
switch (action) {
|
||||
case ViewerKeyboardActions.ToggleModels:
|
||||
toggleActiveControl('models')
|
||||
break
|
||||
case ViewerKeyboardActions.ToggleExplorer:
|
||||
toggleActiveControl('explorer')
|
||||
break
|
||||
case ViewerKeyboardActions.ToggleDiscussions:
|
||||
toggleActiveControl('discussions')
|
||||
break
|
||||
case ViewerKeyboardActions.ToggleMeasurements:
|
||||
toggleMeasurements()
|
||||
break
|
||||
case ViewerKeyboardActions.ToggleProjection:
|
||||
trackAndtoggleProjection()
|
||||
break
|
||||
case ViewerKeyboardActions.ToggleSectionBox:
|
||||
toggleSectionBox()
|
||||
break
|
||||
case ViewerKeyboardActions.ZoomExtentsOrSelection:
|
||||
trackAndzoomExtentsOrSelection()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Object.entries(map).forEach(([actionKey, [modifiers, key]]) => {
|
||||
const action = actionKey as ViewerKeyboardActions
|
||||
onKeyboardShortcut(modifiers, key, () => handleKeyboardAction(action))
|
||||
})
|
||||
|
||||
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
|
||||
|
||||
const toggleActiveControl = (control: ActiveControl) => {
|
||||
|
@ -396,33 +450,6 @@ const toggleActiveControl = (control: ActiveControl) => {
|
|||
activeControl.value = activeControl.value === control ? 'none' : control
|
||||
}
|
||||
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], 'm', () => {
|
||||
toggleActiveControl('models')
|
||||
})
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], 'e', () => {
|
||||
toggleActiveControl('explorer')
|
||||
})
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], 'f', () => {
|
||||
toggleActiveControl('filters')
|
||||
})
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], ['t'], () => {
|
||||
toggleActiveControl('discussions')
|
||||
})
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], 'd', () => {
|
||||
toggleActiveControl('measurements')
|
||||
})
|
||||
|
||||
// Viewer actions kbd shortcuts
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], ' ', () => {
|
||||
trackAndzoomExtentsOrSelection()
|
||||
})
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], 'p', () => {
|
||||
toggleProjection()
|
||||
})
|
||||
onKeyboardShortcut([ModifierKeys.AltOrOpt], 'b', () => {
|
||||
toggleSectionBox()
|
||||
})
|
||||
|
||||
const mp = useMixpanel()
|
||||
watch(activeControl, (newVal) => {
|
||||
mp.track('Viewer Action', { type: 'action', name: 'controls-toggle', action: newVal })
|
||||
|
|
|
@ -26,7 +26,7 @@ const props = defineProps<{
|
|||
}>()
|
||||
|
||||
const { result, loading, load } = useLazyQuery(viewerRawObjectQuery, () => ({
|
||||
streamId: projectId.value,
|
||||
projectId: projectId.value,
|
||||
objectId: props.object['referencedId'] as string
|
||||
}))
|
||||
|
||||
|
@ -35,7 +35,7 @@ if (props.object['referencedId']) {
|
|||
}
|
||||
|
||||
const kvps = computed(() => {
|
||||
const obj = (result.value?.stream?.object?.data || props.object) as Record<
|
||||
const obj = (result.value?.project?.object?.data || props.object) as Record<
|
||||
string,
|
||||
unknown
|
||||
>
|
||||
|
|
|
@ -307,8 +307,18 @@ const isNonEmptyObjectArray = (x: unknown) => isNonEmptyArray(x) && isObject(x[0
|
|||
const isObject = (x: unknown) =>
|
||||
typeof x === 'object' && !Array.isArray(x) && x !== null
|
||||
|
||||
const isAllowedType = (node: ExplorerNode) =>
|
||||
!['Objects.Other.DisplayStyle'].includes(node.raw?.speckle_type || '')
|
||||
const hiddenSpeckleTypes = [
|
||||
'Objects.Other.DisplayStyle',
|
||||
'Objects.Other.Revit.RevitMaterial',
|
||||
'Objects.BuiltElements.Revit.ProjectInfo',
|
||||
'Objects.BuiltElements.View',
|
||||
'Objects.BuiltElements.View3D'
|
||||
]
|
||||
|
||||
const isAllowedType = (node: ExplorerNode) => {
|
||||
const speckleType = node.raw?.speckle_type || ''
|
||||
return !hiddenSpeckleTypes.some((substring) => speckleType.includes(substring))
|
||||
}
|
||||
|
||||
const unfold = ref(false)
|
||||
|
||||
|
|
|
@ -194,7 +194,7 @@ const documents = {
|
|||
"\n subscription OnProjectAutomationsUpdated($id: String!) {\n projectAutomationsUpdated(projectId: $id) {\n type\n automationId\n automation {\n id\n ...ProjectPageAutomationPage_Automation\n ...ProjectPageAutomationsRow_Automation\n }\n }\n }\n": types.OnProjectAutomationsUpdatedDocument,
|
||||
"\n mutation ServerInfoUpdate($info: ServerInfoUpdateInput!) {\n serverInfoUpdate(info: $info)\n }\n": types.ServerInfoUpdateDocument,
|
||||
"\n mutation AdminPanelDeleteUser($userConfirmation: UserDeleteInput!) {\n adminDeleteUser(userConfirmation: $userConfirmation)\n }\n": types.AdminPanelDeleteUserDocument,
|
||||
"\n mutation AdminPanelDeleteProject($ids: [String!]) {\n streamsDelete(ids: $ids)\n }\n": types.AdminPanelDeleteProjectDocument,
|
||||
"\n mutation AdminPanelDeleteProject($ids: [String!]!) {\n projectMutations {\n batchDelete(ids: $ids)\n }\n }\n": types.AdminPanelDeleteProjectDocument,
|
||||
"\n mutation AdminPanelResendInvite($inviteId: String!) {\n inviteResend(inviteId: $inviteId)\n }\n": types.AdminPanelResendInviteDocument,
|
||||
"\n mutation AdminPanelDeleteInvite($inviteId: String!) {\n inviteDelete(inviteId: $inviteId)\n }\n": types.AdminPanelDeleteInviteDocument,
|
||||
"\n mutation AdminChangeUseRole($userRoleInput: UserRoleInput!) {\n userRoleChange(userRoleInput: $userRoleInput)\n }\n": types.AdminChangeUseRoleDocument,
|
||||
|
@ -224,14 +224,14 @@ const documents = {
|
|||
"\n query ViewerModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n role\n model(id: $modelId) {\n id\n versions(cursor: $versionsCursor, limit: 5) {\n totalCount\n cursor\n items {\n ...ViewerModelVersionCardItem\n }\n }\n }\n }\n }\n": types.ViewerModelVersionsDocument,
|
||||
"\n query ViewerDiffVersions(\n $projectId: String!\n $modelId: String!\n $versionAId: String!\n $versionBId: String!\n ) {\n project(id: $projectId) {\n id\n model(id: $modelId) {\n id\n versionA: version(id: $versionAId) {\n ...ViewerModelVersionCardItem\n }\n versionB: version(id: $versionBId) {\n ...ViewerModelVersionCardItem\n }\n }\n }\n }\n": types.ViewerDiffVersionsDocument,
|
||||
"\n query ViewerLoadedThreads(\n $projectId: String!\n $filter: ProjectCommentsFilter!\n $cursor: String\n $limit: Int = 25\n ) {\n project(id: $projectId) {\n id\n commentThreads(filter: $filter, cursor: $cursor, limit: $limit) {\n totalCount\n totalArchivedCount\n items {\n ...ViewerCommentThread\n ...LinkableComment\n }\n }\n }\n }\n": types.ViewerLoadedThreadsDocument,
|
||||
"\n query Stream($streamId: String!, $objectId: String!) {\n stream(id: $streamId) {\n id\n object(id: $objectId) {\n id\n data\n }\n }\n }\n": types.StreamDocument,
|
||||
"\n query ViewerRawProjectObject($projectId: String!, $objectId: String!) {\n project(id: $projectId) {\n id\n object(id: $objectId) {\n id\n data\n }\n }\n }\n": types.ViewerRawProjectObjectDocument,
|
||||
"\n subscription OnViewerUserActivityBroadcasted(\n $target: ViewerUpdateTrackingTarget!\n $sessionId: String!\n ) {\n viewerUserActivityBroadcasted(target: $target, sessionId: $sessionId) {\n userName\n userId\n user {\n ...LimitedUserAvatar\n }\n state\n status\n sessionId\n }\n }\n": types.OnViewerUserActivityBroadcastedDocument,
|
||||
"\n subscription OnViewerCommentsUpdated($target: ViewerUpdateTrackingTarget!) {\n projectCommentsUpdated(target: $target) {\n id\n type\n comment {\n id\n parent {\n id\n }\n ...ViewerCommentThread\n }\n }\n }\n": types.OnViewerCommentsUpdatedDocument,
|
||||
"\n fragment LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\n }\n": types.LinkableCommentFragmentDoc,
|
||||
"\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n stream(id: $streamId) {\n branch(name: $branchName) {\n id\n }\n }\n }\n": types.LegacyBranchRedirectMetadataDocument,
|
||||
"\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n stream(id: $streamId) {\n commit(id: $commitId) {\n id\n branch {\n id\n }\n }\n }\n }\n": types.LegacyViewerCommitRedirectMetadataDocument,
|
||||
"\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n stream(id: $streamId) {\n id\n commits(limit: 1) {\n totalCount\n items {\n id\n branch {\n id\n }\n }\n }\n }\n }\n": types.LegacyViewerStreamRedirectMetadataDocument,
|
||||
"\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n comment(id: $commentId, streamId: $projectId) {\n ...LinkableComment\n }\n }\n": types.ResolveCommentLinkDocument,
|
||||
"\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n project(id: $streamId) {\n modelByName(name: $branchName) {\n id\n }\n }\n }\n": types.LegacyBranchRedirectMetadataDocument,
|
||||
"\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n project(id: $streamId) {\n version(id: $commitId) {\n id\n model {\n id\n }\n }\n }\n }\n": types.LegacyViewerCommitRedirectMetadataDocument,
|
||||
"\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n project(id: $streamId) {\n id\n versions(limit: 1) {\n totalCount\n items {\n id\n model {\n id\n }\n }\n }\n }\n }\n": types.LegacyViewerStreamRedirectMetadataDocument,
|
||||
"\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n project(id: $projectId) {\n comment(id: $commentId) {\n id\n ...LinkableComment\n }\n }\n }\n": types.ResolveCommentLinkDocument,
|
||||
"\n fragment AutomateFunctionPage_AutomateFunction on AutomateFunction {\n id\n name\n description\n logo\n supportedSourceApps\n tags\n ...AutomateFunctionPageHeader_Function\n ...AutomateFunctionPageInfo_AutomateFunction\n ...AutomateAutomationCreateDialog_AutomateFunction\n creator {\n id\n }\n }\n": types.AutomateFunctionPage_AutomateFunctionFragmentDoc,
|
||||
"\n query AutomateFunctionPage($functionId: ID!) {\n automateFunction(id: $functionId) {\n ...AutomateFunctionPage_AutomateFunction\n }\n }\n": types.AutomateFunctionPageDocument,
|
||||
"\n query AutomateFunctionsPage($search: String, $cursor: String = null) {\n ...AutomateFunctionsPageItems_Query\n ...AutomateFunctionsPageHeader_Query\n }\n": types.AutomateFunctionsPageDocument,
|
||||
|
@ -982,7 +982,7 @@ export function graphql(source: "\n mutation AdminPanelDeleteUser($userConfirma
|
|||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation AdminPanelDeleteProject($ids: [String!]) {\n streamsDelete(ids: $ids)\n }\n"): (typeof documents)["\n mutation AdminPanelDeleteProject($ids: [String!]) {\n streamsDelete(ids: $ids)\n }\n"];
|
||||
export function graphql(source: "\n mutation AdminPanelDeleteProject($ids: [String!]!) {\n projectMutations {\n batchDelete(ids: $ids)\n }\n }\n"): (typeof documents)["\n mutation AdminPanelDeleteProject($ids: [String!]!) {\n projectMutations {\n batchDelete(ids: $ids)\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
@ -1102,7 +1102,7 @@ export function graphql(source: "\n query ViewerLoadedThreads(\n $projectId:
|
|||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query Stream($streamId: String!, $objectId: String!) {\n stream(id: $streamId) {\n id\n object(id: $objectId) {\n id\n data\n }\n }\n }\n"): (typeof documents)["\n query Stream($streamId: String!, $objectId: String!) {\n stream(id: $streamId) {\n id\n object(id: $objectId) {\n id\n data\n }\n }\n }\n"];
|
||||
export function graphql(source: "\n query ViewerRawProjectObject($projectId: String!, $objectId: String!) {\n project(id: $projectId) {\n id\n object(id: $objectId) {\n id\n data\n }\n }\n }\n"): (typeof documents)["\n query ViewerRawProjectObject($projectId: String!, $objectId: String!) {\n project(id: $projectId) {\n id\n object(id: $objectId) {\n id\n data\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
@ -1118,19 +1118,19 @@ export function graphql(source: "\n fragment LinkableComment on Comment {\n
|
|||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n stream(id: $streamId) {\n branch(name: $branchName) {\n id\n }\n }\n }\n"): (typeof documents)["\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n stream(id: $streamId) {\n branch(name: $branchName) {\n id\n }\n }\n }\n"];
|
||||
export function graphql(source: "\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n project(id: $streamId) {\n modelByName(name: $branchName) {\n id\n }\n }\n }\n"): (typeof documents)["\n query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {\n project(id: $streamId) {\n modelByName(name: $branchName) {\n id\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n stream(id: $streamId) {\n commit(id: $commitId) {\n id\n branch {\n id\n }\n }\n }\n }\n"): (typeof documents)["\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n stream(id: $streamId) {\n commit(id: $commitId) {\n id\n branch {\n id\n }\n }\n }\n }\n"];
|
||||
export function graphql(source: "\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n project(id: $streamId) {\n version(id: $commitId) {\n id\n model {\n id\n }\n }\n }\n }\n"): (typeof documents)["\n query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {\n project(id: $streamId) {\n version(id: $commitId) {\n id\n model {\n id\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n stream(id: $streamId) {\n id\n commits(limit: 1) {\n totalCount\n items {\n id\n branch {\n id\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n stream(id: $streamId) {\n id\n commits(limit: 1) {\n totalCount\n items {\n id\n branch {\n id\n }\n }\n }\n }\n }\n"];
|
||||
export function graphql(source: "\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n project(id: $streamId) {\n id\n versions(limit: 1) {\n totalCount\n items {\n id\n model {\n id\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query LegacyViewerStreamRedirectMetadata($streamId: String!) {\n project(id: $streamId) {\n id\n versions(limit: 1) {\n totalCount\n items {\n id\n model {\n id\n }\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n comment(id: $commentId, streamId: $projectId) {\n ...LinkableComment\n }\n }\n"): (typeof documents)["\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n comment(id: $commentId, streamId: $projectId) {\n ...LinkableComment\n }\n }\n"];
|
||||
export function graphql(source: "\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n project(id: $projectId) {\n comment(id: $commentId) {\n id\n ...LinkableComment\n }\n }\n }\n"): (typeof documents)["\n query ResolveCommentLink($commentId: String!, $projectId: String!) {\n project(id: $projectId) {\n comment(id: $commentId) {\n id\n ...LinkableComment\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -13,8 +13,10 @@ export const adminDeleteUserMutation = graphql(`
|
|||
`)
|
||||
|
||||
export const adminDeleteProjectMutation = graphql(`
|
||||
mutation AdminPanelDeleteProject($ids: [String!]) {
|
||||
streamsDelete(ids: $ids)
|
||||
mutation AdminPanelDeleteProject($ids: [String!]!) {
|
||||
projectMutations {
|
||||
batchDelete(ids: $ids)
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
|
|
|
@ -136,8 +136,8 @@ export const viewerLoadedThreadsQuery = graphql(`
|
|||
`)
|
||||
|
||||
export const viewerRawObjectQuery = graphql(`
|
||||
query Stream($streamId: String!, $objectId: String!) {
|
||||
stream(id: $streamId) {
|
||||
query ViewerRawProjectObject($projectId: String!, $objectId: String!) {
|
||||
project(id: $projectId) {
|
||||
id
|
||||
object(id: $objectId) {
|
||||
id
|
||||
|
|
|
@ -14,8 +14,8 @@ import { ViewerHashStateKeys } from '~/lib/viewer/composables/setup/urlHashState
|
|||
|
||||
const legacyBranchMetadataQuery = graphql(`
|
||||
query LegacyBranchRedirectMetadata($streamId: String!, $branchName: String!) {
|
||||
stream(id: $streamId) {
|
||||
branch(name: $branchName) {
|
||||
project(id: $streamId) {
|
||||
modelByName(name: $branchName) {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ const legacyBranchMetadataQuery = graphql(`
|
|||
|
||||
const legacyViewerCommitMetadataQuery = graphql(`
|
||||
query LegacyViewerCommitRedirectMetadata($streamId: String!, $commitId: String!) {
|
||||
stream(id: $streamId) {
|
||||
commit(id: $commitId) {
|
||||
project(id: $streamId) {
|
||||
version(id: $commitId) {
|
||||
id
|
||||
branch {
|
||||
model {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
@ -37,13 +37,13 @@ const legacyViewerCommitMetadataQuery = graphql(`
|
|||
|
||||
const legacyViewerStreamMetadataQuery = graphql(`
|
||||
query LegacyViewerStreamRedirectMetadata($streamId: String!) {
|
||||
stream(id: $streamId) {
|
||||
project(id: $streamId) {
|
||||
id
|
||||
commits(limit: 1) {
|
||||
versions(limit: 1) {
|
||||
totalCount
|
||||
items {
|
||||
id
|
||||
branch {
|
||||
model {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ const adminPageRgx = /^\/admin\/?/
|
|||
*/
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to) => {
|
||||
const logger = useLogger()
|
||||
const path = to.path
|
||||
const apollo = useApolloClientFromNuxt()
|
||||
const resourceBuilder = () => SpeckleViewer.ViewerRoute.resourceBuilder()
|
||||
|
@ -91,23 +92,33 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||
const resourceIdString = resourceIdStringBuilder.addObject(viewerId).toString()
|
||||
return navigateTo(modelRoute(viewerStreamId, resourceIdString, hashState))
|
||||
} else {
|
||||
const { data } = await apollo
|
||||
const { data, errors } = await apollo
|
||||
.query({
|
||||
query: legacyViewerCommitMetadataQuery,
|
||||
variables: { streamId: viewerStreamId, commitId: viewerId }
|
||||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
const branchId = data?.stream?.commit?.branch?.id
|
||||
const branchId = data?.project?.version?.model?.id
|
||||
|
||||
return navigateTo(
|
||||
branchId
|
||||
? modelRoute(
|
||||
viewerStreamId,
|
||||
resourceIdStringBuilder.addModel(branchId, viewerId).toString(),
|
||||
hashState
|
||||
)
|
||||
: projectRoute(viewerStreamId)
|
||||
)
|
||||
if (branchId) {
|
||||
return navigateTo(
|
||||
modelRoute(
|
||||
viewerStreamId,
|
||||
resourceIdStringBuilder.addModel(branchId, viewerId).toString(),
|
||||
hashState
|
||||
)
|
||||
)
|
||||
} else {
|
||||
logger.warn(
|
||||
{
|
||||
errors,
|
||||
streamId: viewerStreamId,
|
||||
commitId: viewerId
|
||||
},
|
||||
"Couldn't resolve legacy viewer redirect commit metadata"
|
||||
)
|
||||
return navigateTo(projectRoute(viewerStreamId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,6 +142,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||
const branchName = to.query['branch'] as Optional<string> // get first branch commit
|
||||
|
||||
if (!streamId?.length) {
|
||||
logger.warn('No stream ID provided for embed viewer redirect')
|
||||
return navigateTo(homeRoute)
|
||||
}
|
||||
|
||||
|
@ -141,27 +153,37 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||
})
|
||||
)
|
||||
} else if (commitId?.length) {
|
||||
const { data } = await apollo
|
||||
const { data, errors } = await apollo
|
||||
.query({
|
||||
query: legacyViewerCommitMetadataQuery,
|
||||
variables: { streamId, commitId }
|
||||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
const branchId = data?.stream?.commit?.branch?.id
|
||||
const branchId = data?.project?.version?.model?.id
|
||||
|
||||
return navigateTo(
|
||||
branchId
|
||||
? modelRoute(
|
||||
streamId,
|
||||
resourceBuilder().addModel(branchId, commitId).toString(),
|
||||
{
|
||||
[ViewerHashStateKeys.EmbedOptions]: JSON.stringify(embedOptions)
|
||||
}
|
||||
)
|
||||
: projectRoute(viewerStreamId)
|
||||
)
|
||||
if (branchId) {
|
||||
return navigateTo(
|
||||
modelRoute(
|
||||
streamId,
|
||||
resourceBuilder().addModel(branchId, commitId).toString(),
|
||||
{
|
||||
[ViewerHashStateKeys.EmbedOptions]: JSON.stringify(embedOptions)
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
logger.warn(
|
||||
{
|
||||
errors,
|
||||
streamId,
|
||||
commitId
|
||||
},
|
||||
"Couldn't resolve legacy commit embed redirect metadata"
|
||||
)
|
||||
return navigateTo(projectRoute(streamId))
|
||||
}
|
||||
} else if (branchName?.length) {
|
||||
const { data } = await apollo
|
||||
const { data, errors } = await apollo
|
||||
.query({
|
||||
query: legacyBranchMetadataQuery,
|
||||
variables: {
|
||||
|
@ -171,44 +193,63 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
|
||||
return navigateTo(
|
||||
data?.stream?.branch?.id
|
||||
? modelRoute(
|
||||
streamId,
|
||||
resourceBuilder().addModel(data.stream.branch.id).toString(),
|
||||
{
|
||||
[ViewerHashStateKeys.EmbedOptions]: JSON.stringify(embedOptions)
|
||||
}
|
||||
)
|
||||
: projectRoute(streamId)
|
||||
)
|
||||
const branchId = data?.project?.modelByName?.id
|
||||
if (branchId) {
|
||||
return navigateTo(
|
||||
modelRoute(streamId, resourceBuilder().addModel(branchId).toString(), {
|
||||
[ViewerHashStateKeys.EmbedOptions]: JSON.stringify(embedOptions)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
logger.warn(
|
||||
{
|
||||
errors,
|
||||
streamId,
|
||||
branchName: decodeURIComponent(branchName)
|
||||
},
|
||||
"Couldn't resolve legacy branch embed redirect metadata"
|
||||
)
|
||||
return navigateTo(projectRoute(streamId))
|
||||
}
|
||||
} else {
|
||||
const { data } = await apollo
|
||||
const { data, errors } = await apollo
|
||||
.query({ query: legacyViewerStreamMetadataQuery, variables: { streamId } })
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
|
||||
return navigateTo(
|
||||
data?.stream?.commits?.items?.length && data.stream.commits.items[0].branch
|
||||
? modelRoute(
|
||||
data.stream.id,
|
||||
SpeckleViewer.ViewerRoute.resourceBuilder()
|
||||
.addModel(
|
||||
data.stream.commits.items[0].branch.id,
|
||||
data.stream.commits.items[0].id
|
||||
)
|
||||
.toString(),
|
||||
{
|
||||
[ViewerHashStateKeys.EmbedOptions]: JSON.stringify(embedOptions)
|
||||
}
|
||||
)
|
||||
: projectRoute(streamId)
|
||||
)
|
||||
if (
|
||||
data?.project?.versions?.items?.length &&
|
||||
data.project.versions.items[0].model
|
||||
) {
|
||||
return navigateTo(
|
||||
modelRoute(
|
||||
data.project.id,
|
||||
SpeckleViewer.ViewerRoute.resourceBuilder()
|
||||
.addModel(
|
||||
data.project.versions.items[0].model.id,
|
||||
data.project.versions.items[0].id
|
||||
)
|
||||
.toString(),
|
||||
{
|
||||
[ViewerHashStateKeys.EmbedOptions]: JSON.stringify(embedOptions)
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
logger.warn(
|
||||
{
|
||||
errors,
|
||||
streamId
|
||||
},
|
||||
"Couldn't resolve legacy stream embed redirect metadata"
|
||||
)
|
||||
return navigateTo(projectRoute(streamId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [, branchStreamId, branchName] = path.match(streamBranchPageRgx) || []
|
||||
if (branchStreamId && branchName) {
|
||||
const { data } = await apollo
|
||||
const { data, errors } = await apollo
|
||||
.query({
|
||||
query: legacyBranchMetadataQuery,
|
||||
variables: {
|
||||
|
@ -217,13 +258,22 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||
}
|
||||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
const branchId = data?.stream?.branch?.id
|
||||
const branchId = data?.project?.modelByName?.id
|
||||
|
||||
return navigateTo(
|
||||
branchId
|
||||
? modelVersionsRoute(branchStreamId, branchId)
|
||||
: projectRoute(branchStreamId)
|
||||
)
|
||||
if (branchId) {
|
||||
return navigateTo(modelVersionsRoute(branchStreamId, branchId))
|
||||
} else {
|
||||
logger.warn(
|
||||
{
|
||||
errors,
|
||||
streamId: branchStreamId,
|
||||
branchName: decodeURIComponent(branchName)
|
||||
},
|
||||
"Couldn't resolve legacy branch redirect metadata"
|
||||
)
|
||||
|
||||
return navigateTo(projectRoute(branchStreamId))
|
||||
}
|
||||
}
|
||||
|
||||
const [, streamId] = path.match(streamPageRgx) || []
|
||||
|
|
|
@ -5,8 +5,11 @@ import { getLinkToThread } from '~/lib/viewer/helpers/comments'
|
|||
|
||||
const resolveLinkQuery = graphql(`
|
||||
query ResolveCommentLink($commentId: String!, $projectId: String!) {
|
||||
comment(id: $commentId, streamId: $projectId) {
|
||||
...LinkableComment
|
||||
project(id: $projectId) {
|
||||
comment(id: $commentId) {
|
||||
id
|
||||
...LinkableComment
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
@ -26,7 +29,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
|||
})
|
||||
.catch(convertThrowIntoFetchResult)
|
||||
|
||||
const comment = res.data?.comment
|
||||
const comment = res.data?.project?.comment
|
||||
if (!comment) {
|
||||
return abortNavigation(
|
||||
createError({
|
||||
|
|
|
@ -5,10 +5,19 @@ extend type Query {
|
|||
streamAccessRequest(streamId: String!): StreamAccessRequest
|
||||
@hasServerRole(role: SERVER_GUEST)
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use User.projectAccessRequest instead."
|
||||
)
|
||||
}
|
||||
|
||||
extend type User {
|
||||
"""
|
||||
Get pending project access request, that the user made
|
||||
"""
|
||||
projectAccessRequest(projectId: String!): ProjectAccessRequest
|
||||
@hasServerRole(role: SERVER_GUEST)
|
||||
@isOwner
|
||||
}
|
||||
|
||||
extend type Stream {
|
||||
"""
|
||||
Pending stream access requests
|
||||
|
@ -16,10 +25,33 @@ extend type Stream {
|
|||
pendingAccessRequests: [StreamAccessRequest!]
|
||||
@hasStreamRole(role: STREAM_OWNER)
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use Project.pendingAccessRequests instead."
|
||||
)
|
||||
}
|
||||
|
||||
extend type Project {
|
||||
"""
|
||||
Pending project access requests
|
||||
"""
|
||||
pendingAccessRequests: [ProjectAccessRequest!] @hasStreamRole(role: STREAM_OWNER)
|
||||
}
|
||||
|
||||
type ProjectAccessRequestMutations {
|
||||
"""
|
||||
Request access to a specific project
|
||||
"""
|
||||
create(projectId: String!): ProjectAccessRequest!
|
||||
|
||||
"""
|
||||
Accept or decline a project access request. Must be a project owner to invoke this.
|
||||
"""
|
||||
use(
|
||||
requestId: String!
|
||||
accept: Boolean!
|
||||
role: StreamRole! = STREAM_CONTRIBUTOR
|
||||
): Project!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
"""
|
||||
Accept or decline a stream access request. Must be a stream owner to invoke this.
|
||||
|
@ -32,7 +64,7 @@ extend type Mutation {
|
|||
@hasServerRole(role: SERVER_GUEST)
|
||||
@hasScope(scope: "users:invite")
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.use instead."
|
||||
)
|
||||
|
||||
"""
|
||||
|
@ -42,10 +74,19 @@ extend type Mutation {
|
|||
@hasServerRole(role: SERVER_GUEST)
|
||||
@hasScope(scope: "users:invite")
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.create instead."
|
||||
)
|
||||
}
|
||||
|
||||
extend type ProjectMutations {
|
||||
"""
|
||||
Access request related mutations
|
||||
"""
|
||||
accessRequestMutations: ProjectAccessRequestMutations!
|
||||
@hasServerRole(role: SERVER_GUEST)
|
||||
@hasScope(scope: "users:invite")
|
||||
}
|
||||
|
||||
"""
|
||||
Created when a user requests to become a contributor on a stream
|
||||
"""
|
||||
|
@ -60,3 +101,18 @@ type StreamAccessRequest {
|
|||
stream: Stream!
|
||||
createdAt: DateTime!
|
||||
}
|
||||
|
||||
"""
|
||||
Created when a user requests to become a contributor on a project
|
||||
"""
|
||||
type ProjectAccessRequest {
|
||||
id: ID!
|
||||
requester: LimitedUser!
|
||||
requesterId: String!
|
||||
projectId: String!
|
||||
"""
|
||||
Can only be selected if authed user has proper access
|
||||
"""
|
||||
project: Project!
|
||||
createdAt: DateTime!
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
extend type Query {
|
||||
comment(id: String!, streamId: String!): Comment
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use Project.comment instead."
|
||||
)
|
||||
|
||||
"""
|
||||
This query can be used in the following ways:
|
||||
- get all the comments for a stream: **do not pass in any resource identifiers**.
|
||||
|
@ -27,6 +28,11 @@ extend type Project {
|
|||
limit: Int! = 25
|
||||
filter: ProjectCommentsFilter
|
||||
): ProjectCommentCollection!
|
||||
|
||||
"""
|
||||
Get specific project comment/thread by ID
|
||||
"""
|
||||
comment(id: String!): Comment
|
||||
}
|
||||
|
||||
extend type Version {
|
||||
|
|
|
@ -13,7 +13,7 @@ extend type Stream {
|
|||
)
|
||||
branch(name: String = "main"): Branch
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future. Use Project.model instead."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use Project.model or Project.modelByName instead."
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ extend type Mutation {
|
|||
@hasServerRole(role: SERVER_GUEST)
|
||||
@hasScope(scope: "streams:write")
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use VersionMutations.create instead."
|
||||
)
|
||||
|
||||
commitUpdate(commit: CommitUpdateInput!): Boolean!
|
||||
|
@ -120,7 +120,7 @@ extend type Mutation {
|
|||
@hasServerRole(role: SERVER_GUEST)
|
||||
@hasScope(scope: "streams:read")
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use VersionMutations.markReceived instead."
|
||||
)
|
||||
|
||||
commitDelete(commit: CommitDeleteInput!): Boolean!
|
||||
|
|
|
@ -40,6 +40,11 @@ extend type Project {
|
|||
Retrieve a specific project version by its ID
|
||||
"""
|
||||
version(id: String!): Version
|
||||
|
||||
"""
|
||||
Retrieve a specific project model by its ID
|
||||
"""
|
||||
modelByName(name: String!): Model!
|
||||
}
|
||||
|
||||
extend type User {
|
||||
|
@ -174,10 +179,29 @@ input UpdateVersionInput {
|
|||
message: String
|
||||
}
|
||||
|
||||
input CreateVersionInput {
|
||||
projectId: String!
|
||||
modelId: String!
|
||||
objectId: String!
|
||||
message: String
|
||||
sourceApplication: String
|
||||
totalChildrenCount: Int
|
||||
parents: [String!]
|
||||
}
|
||||
|
||||
input MarkReceivedVersionInput {
|
||||
projectId: String!
|
||||
versionId: String!
|
||||
sourceApplication: String!
|
||||
message: String
|
||||
}
|
||||
|
||||
type VersionMutations {
|
||||
moveToModel(input: MoveVersionsInput!): Model!
|
||||
delete(input: DeleteVersionsInput!): Boolean!
|
||||
update(input: UpdateVersionInput!): Version!
|
||||
create(input: CreateVersionInput!): Version!
|
||||
markReceived(input: MarkReceivedVersionInput!): Boolean!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
extend type Stream {
|
||||
object(id: String!): Object
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use Project.object instead."
|
||||
)
|
||||
}
|
||||
|
||||
extend type Project {
|
||||
object(id: String!): Object
|
||||
}
|
||||
|
||||
type Object {
|
||||
id: String!
|
||||
speckleType: String
|
||||
applicationId: String
|
||||
applicationId: String @deprecated(reason: "Not implemented.")
|
||||
createdAt: DateTime
|
||||
totalChildrenCount: Int
|
||||
"""
|
||||
|
@ -32,11 +36,13 @@ type Object {
|
|||
type ObjectCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
objects: [Object]!
|
||||
objects: [Object!]!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
objectCreate(objectInput: ObjectCreateInput!): [String]!
|
||||
objectCreate(objectInput: ObjectCreateInput!): [String!]!
|
||||
@hasServerRole(role: SERVER_GUEST)
|
||||
@hasScope(scope: "streams:write")
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
)
|
||||
|
|
|
@ -99,6 +99,11 @@ type ProjectMutations {
|
|||
"""
|
||||
delete(id: String!): Boolean! @hasServerRole(role: SERVER_USER)
|
||||
|
||||
"""
|
||||
Batch delete projects
|
||||
"""
|
||||
batchDelete(ids: [String!]!): Boolean! @hasServerRole(role: SERVER_ADMIN)
|
||||
|
||||
"""
|
||||
Updates an existing project
|
||||
"""
|
||||
|
|
|
@ -206,7 +206,7 @@ extend type Mutation {
|
|||
streamsDelete(ids: [String!]): Boolean!
|
||||
@hasServerRole(role: SERVER_ADMIN)
|
||||
@deprecated(
|
||||
reason: "Part of the old API surface and will be removed in the future."
|
||||
reason: "Part of the old API surface and will be removed in the future. Use ProjectMutations.batchDelete instead."
|
||||
)
|
||||
|
||||
"""
|
||||
|
|
|
@ -13,11 +13,14 @@ generates:
|
|||
Stream: '@/modules/core/helpers/graphTypes#StreamGraphQLReturn'
|
||||
Commit: '@/modules/core/helpers/graphTypes#CommitGraphQLReturn'
|
||||
Project: '@/modules/core/helpers/graphTypes#ProjectGraphQLReturn'
|
||||
Object: '@/modules/core/helpers/graphTypes#ObjectGraphQLReturn'
|
||||
Version: '@/modules/core/helpers/graphTypes#VersionGraphQLReturn'
|
||||
ServerInvite: '@/modules/core/helpers/graphTypes#ServerInviteGraphQLReturnType'
|
||||
Model: '@/modules/core/helpers/graphTypes#ModelGraphQLReturn'
|
||||
ModelsTreeItem: '@/modules/core/helpers/graphTypes#ModelsTreeItemGraphQLReturn'
|
||||
StreamAccessRequest: '@/modules/accessrequests/helpers/graphTypes#StreamAccessRequestGraphQLReturn'
|
||||
ProjectAccessRequest: '@/modules/accessrequests/helpers/graphTypes#ProjectAccessRequestGraphQLReturn'
|
||||
ProjectAccessRequestMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
LimitedUser: '@/modules/core/helpers/graphTypes#LimitedUserGraphQLReturn'
|
||||
ActiveUserMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
ProjectMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
Adapted from prom-client: https://github.com/siimon/prom-client/tree/master/lib/metrics
|
||||
|
||||
Copyright 2015 Simon Nyberg
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Histogram, Registry } from 'prom-client'
|
||||
import type { Metric } from '@/logging/highFrequencyMetrics/highfrequencyMonitoring'
|
||||
|
||||
const NODEJS_HEAP_SIZE_TOTAL = 'nodejs_heap_size_total_bytes_high_frequency'
|
||||
const NODEJS_HEAP_SIZE_USED = 'nodejs_heap_size_used_bytes_high_frequency'
|
||||
const NODEJS_EXTERNAL_MEMORY = 'nodejs_external_memory_bytes_high_frequency'
|
||||
|
||||
type BucketName =
|
||||
| typeof NODEJS_HEAP_SIZE_TOTAL
|
||||
| typeof NODEJS_HEAP_SIZE_USED
|
||||
| typeof NODEJS_EXTERNAL_MEMORY
|
||||
|
||||
const DEFAULT_NODEJS_HEAP_SIZE_BUCKETS = {
|
||||
NODEJS_HEAP_SIZE_TOTAL: [0, 0.1e9, 0.25e9, 0.5e9, 0.75e9, 1e9, 2e9], //TODO: check if this is the right default
|
||||
NODEJS_HEAP_SIZE_USED: [0, 0.1e9, 0.25e9, 0.5e9, 0.75e9, 1e9, 2e9], //TODO: check if this is the right default
|
||||
NODEJS_EXTERNAL_MEMORY: [0, 0.1e9, 0.25e9, 0.5e9, 0.75e9, 1e9, 2e9] //TODO: check if this is the right default
|
||||
}
|
||||
|
||||
type MetricConfig = {
|
||||
prefix?: string
|
||||
labels?: Record<string, string>
|
||||
buckets?: Record<BucketName, number[]>
|
||||
}
|
||||
|
||||
export const heapSizeAndUsed = (
|
||||
registry: Registry,
|
||||
config: MetricConfig = {}
|
||||
): Metric => {
|
||||
const registers = registry ? [registry] : undefined
|
||||
const namePrefix = config.prefix ?? ''
|
||||
const labels = config.labels ?? {}
|
||||
const labelNames = Object.keys(labels)
|
||||
const buckets = { ...DEFAULT_NODEJS_HEAP_SIZE_BUCKETS, ...config.buckets }
|
||||
|
||||
const heapSizeTotal = new Histogram({
|
||||
name: namePrefix + NODEJS_HEAP_SIZE_TOTAL,
|
||||
help: 'Process heap size from Node.js in bytes. This data is collected at a higher frequency than Prometheus scrapes, and is presented as a Histogram.',
|
||||
registers,
|
||||
buckets: buckets.NODEJS_HEAP_SIZE_TOTAL,
|
||||
labelNames
|
||||
})
|
||||
const heapSizeUsed = new Histogram({
|
||||
name: namePrefix + NODEJS_HEAP_SIZE_USED,
|
||||
help: 'Process heap size used from Node.js in bytes. This data is collected at a higher frequency than Prometheus scrapes, and is presented as a Histogram.',
|
||||
registers,
|
||||
buckets: buckets.NODEJS_HEAP_SIZE_USED,
|
||||
labelNames
|
||||
})
|
||||
const externalMemUsed = new Histogram({
|
||||
name: namePrefix + NODEJS_EXTERNAL_MEMORY,
|
||||
help: 'Node.js external memory size in bytes. This data is collected at a higher frequency than Prometheus scrapes, and is presented as a Histogram.',
|
||||
registers,
|
||||
buckets: buckets.NODEJS_EXTERNAL_MEMORY,
|
||||
labelNames
|
||||
})
|
||||
|
||||
return {
|
||||
collect: () => {
|
||||
const memUsage = safeMemoryUsage()
|
||||
if (memUsage) {
|
||||
heapSizeTotal.observe(labels, memUsage.heapTotal)
|
||||
heapSizeUsed.observe(labels, memUsage.heapUsed)
|
||||
if (memUsage.external !== undefined) {
|
||||
externalMemUsed.observe(labels, memUsage.external)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function safeMemoryUsage() {
|
||||
try {
|
||||
return process.memoryUsage()
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* High frequency monitoring, collects data related to CPU, memory, and network usage
|
||||
* at a higher frequency than the default prometheus monitoring. It makes the data
|
||||
* available to Prometheus via an histogram.
|
||||
*/
|
||||
|
||||
import { Histogram, Registry } from 'prom-client'
|
||||
import { processCpuTotal } from '@/logging/highFrequencyMetrics/processCPUTotal'
|
||||
import { heapSizeAndUsed } from '@/logging/highFrequencyMetrics/heapSizeAndUsed'
|
||||
|
||||
type MetricConfig = {
|
||||
prefix?: string
|
||||
labels?: Record<string, string>
|
||||
buckets?: Record<string, number[]>
|
||||
}
|
||||
|
||||
type HighFrequencyMonitor = {
|
||||
start: () => () => void
|
||||
}
|
||||
|
||||
export const initHighFrequencyMonitoring = (params: {
|
||||
register: Registry
|
||||
collectionPeriodMilliseconds: number
|
||||
config?: MetricConfig
|
||||
}): HighFrequencyMonitor => {
|
||||
const { register, collectionPeriodMilliseconds } = params
|
||||
const config = params.config ?? {}
|
||||
const registers = register ? [register] : undefined
|
||||
const namePrefix = config.prefix ?? ''
|
||||
const labels = config.labels ?? {}
|
||||
const labelNames = Object.keys(labels)
|
||||
|
||||
const metrics = [processCpuTotal(register, config), heapSizeAndUsed(register, config)]
|
||||
|
||||
const selfMonitor = new Histogram({
|
||||
name: namePrefix + 'self_monitor_time_high_frequency',
|
||||
help: 'The time taken to collect all of the high frequency metrics, seconds.',
|
||||
registers,
|
||||
buckets: [0, 0.001, 0.01, 0.025, 0.05, 0.1, 0.2],
|
||||
labelNames
|
||||
})
|
||||
|
||||
return {
|
||||
start: collectHighFrequencyMetrics({
|
||||
selfMonitor,
|
||||
metrics,
|
||||
collectionPeriodMilliseconds
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export interface Metric {
|
||||
collect: () => void
|
||||
}
|
||||
|
||||
const collectHighFrequencyMetrics = (params: {
|
||||
selfMonitor: Histogram<string>
|
||||
collectionPeriodMilliseconds: number
|
||||
metrics: Metric[]
|
||||
}) => {
|
||||
const { selfMonitor, metrics, collectionPeriodMilliseconds } = params
|
||||
return () => {
|
||||
const intervalId = setInterval(() => {
|
||||
const end = selfMonitor.startTimer()
|
||||
for (const metric of metrics) {
|
||||
metric.collect()
|
||||
}
|
||||
end()
|
||||
}, collectionPeriodMilliseconds)
|
||||
return () => clearInterval(intervalId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Adapted from prom-client: https://github.com/siimon/prom-client/tree/master/lib/metrics
|
||||
*
|
||||
Copyright 2015 Simon Nyberg
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Histogram, Registry } from 'prom-client'
|
||||
import type { Metric } from '@/logging/highFrequencyMetrics/highfrequencyMonitoring'
|
||||
|
||||
const PROCESS_CPU_USER_SECONDS = 'process_cpu_user_seconds_total_high_frequency'
|
||||
const PROCESS_CPU_SYSTEM_SECONDS = 'process_cpu_system_seconds_total_high_frequency'
|
||||
const PROCESS_CPU_SECONDS = 'process_cpu_seconds_total_high_frequency'
|
||||
|
||||
type BucketName =
|
||||
| typeof PROCESS_CPU_USER_SECONDS
|
||||
| typeof PROCESS_CPU_SYSTEM_SECONDS
|
||||
| typeof PROCESS_CPU_SECONDS
|
||||
|
||||
const DEFAULT_CPU_TOTAL_BUCKETS = {
|
||||
PROCESS_CPU_SECONDS: [0, 0.1, 0.25, 0.5, 0.75, 1, 2], //TODO: check if this is the right default
|
||||
PROCESS_CPU_USER_SECONDS: [0, 0.1, 0.25, 0.5, 0.75, 1, 2], //TODO: check if this is the right default
|
||||
PROCESS_CPU_SYSTEM_SECONDS: [0, 0.1, 0.25, 0.5, 0.75, 1, 2] //TODO: check if this is the right default
|
||||
}
|
||||
|
||||
type MetricConfig = {
|
||||
prefix?: string
|
||||
labels?: Record<string, string>
|
||||
buckets?: Record<BucketName, number[]>
|
||||
}
|
||||
|
||||
export const processCpuTotal = (
|
||||
registry: Registry,
|
||||
config: MetricConfig = {}
|
||||
): Metric => {
|
||||
const registers = registry ? [registry] : undefined
|
||||
const namePrefix = config.prefix ?? ''
|
||||
const labels = config.labels ?? {}
|
||||
const labelNames = Object.keys(labels)
|
||||
const buckets = { ...DEFAULT_CPU_TOTAL_BUCKETS, ...config.buckets }
|
||||
|
||||
const cpuUserUsageHistogram = new Histogram({
|
||||
name: namePrefix + PROCESS_CPU_USER_SECONDS,
|
||||
help: 'Total user CPU time spent in seconds. This data is collected at a higher frequency than Prometheus scrapes, and is presented as a Histogram.',
|
||||
labelNames,
|
||||
buckets: buckets.PROCESS_CPU_USER_SECONDS,
|
||||
registers
|
||||
})
|
||||
const cpuSystemUsageHistogram = new Histogram({
|
||||
name: namePrefix + PROCESS_CPU_SYSTEM_SECONDS,
|
||||
help: 'Total system CPU time spent in seconds. This data is collected at a higher frequency than Prometheus scrapes, and is presented as a Histogram.',
|
||||
registers,
|
||||
buckets: buckets.PROCESS_CPU_SYSTEM_SECONDS,
|
||||
labelNames
|
||||
})
|
||||
const cpuUsageHistogram = new Histogram({
|
||||
name: namePrefix + PROCESS_CPU_SECONDS,
|
||||
help: 'Total user and system CPU time spent in seconds. This data is collected at a higher frequency than Prometheus scrapes, and is presented as a Histogram.',
|
||||
registers,
|
||||
buckets: buckets.PROCESS_CPU_USER_SECONDS,
|
||||
labelNames
|
||||
})
|
||||
|
||||
let lastCpuUsage = process.cpuUsage()
|
||||
|
||||
return {
|
||||
collect: () => {
|
||||
const cpuUsage = process.cpuUsage()
|
||||
|
||||
const userUsageMicros = cpuUsage.user - lastCpuUsage.user
|
||||
const systemUsageMicros = cpuUsage.system - lastCpuUsage.system
|
||||
|
||||
lastCpuUsage = cpuUsage
|
||||
|
||||
cpuUserUsageHistogram.observe(labels, userUsageMicros / 1e6)
|
||||
cpuSystemUsageHistogram.observe(labels, systemUsageMicros / 1e6)
|
||||
cpuUsageHistogram.observe(labels, (userUsageMicros + systemUsageMicros) / 1e6)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,10 @@ const { getMachineId } = require('./machineId')
|
|||
const prometheusClient = require('prom-client')
|
||||
const promBundle = require('express-prom-bundle')
|
||||
|
||||
const { initKnexPrometheusMetrics } = require('./knexMonitoring')
|
||||
const { initKnexPrometheusMetrics } = require('@/logging/knexMonitoring')
|
||||
const {
|
||||
initHighFrequencyMonitoring
|
||||
} = require('@/logging/highFrequencyMetrics/highfrequencyMonitoring')
|
||||
|
||||
let prometheusInitialized = false
|
||||
|
||||
|
@ -20,6 +23,11 @@ module.exports = function (app) {
|
|||
app: 'server'
|
||||
})
|
||||
prometheusClient.collectDefaultMetrics()
|
||||
const highfrequencyMonitoring = initHighFrequencyMonitoring({
|
||||
register: prometheusClient.register,
|
||||
collectionPeriodMilliseconds: 100
|
||||
})
|
||||
highfrequencyMonitoring.start()
|
||||
|
||||
initKnexPrometheusMetrics()
|
||||
const expressMetricsMiddleware = promBundle({
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { AccessRequestType } from '@/modules/accessrequests/repositories'
|
||||
import {
|
||||
getPendingProjectRequests,
|
||||
getPendingStreamRequests,
|
||||
getUserProjectAccessRequest,
|
||||
getUserStreamAccessRequest,
|
||||
processPendingProjectRequest,
|
||||
processPendingStreamRequest,
|
||||
requestProjectAccess,
|
||||
requestStreamAccess
|
||||
} from '@/modules/accessrequests/services/stream'
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
|
@ -35,6 +40,35 @@ const resolvers: Resolvers = {
|
|||
return await requestStreamAccess(userId, streamId)
|
||||
}
|
||||
},
|
||||
ProjectMutations: {
|
||||
accessRequestMutations: () => ({})
|
||||
},
|
||||
ProjectAccessRequestMutations: {
|
||||
async create(_parent, args, ctx) {
|
||||
const { userId } = ctx
|
||||
const { projectId } = args
|
||||
return await requestProjectAccess(userId!, projectId)
|
||||
},
|
||||
async use(_parent, args, ctx) {
|
||||
const { userId, resourceAccessRules } = ctx
|
||||
const { requestId, accept, role } = args
|
||||
|
||||
const usedReq = await processPendingProjectRequest(
|
||||
userId!,
|
||||
requestId,
|
||||
accept,
|
||||
mapStreamRoleToValue(role),
|
||||
resourceAccessRules
|
||||
)
|
||||
|
||||
const project = await ctx.loaders.streams.getStream.load(usedReq.resourceId)
|
||||
if (!project) {
|
||||
throw new LogicError('Unexpectedly unable to find request project')
|
||||
}
|
||||
|
||||
return project
|
||||
}
|
||||
},
|
||||
Query: {
|
||||
async streamAccessRequest(_, args, ctx) {
|
||||
const { streamId } = args
|
||||
|
@ -44,12 +78,26 @@ const resolvers: Resolvers = {
|
|||
return await getUserStreamAccessRequest(userId, streamId)
|
||||
}
|
||||
},
|
||||
User: {
|
||||
async projectAccessRequest(parent, args) {
|
||||
const { id: userId } = parent
|
||||
const { projectId } = args
|
||||
|
||||
return await getUserProjectAccessRequest(userId, projectId)
|
||||
}
|
||||
},
|
||||
Stream: {
|
||||
async pendingAccessRequests(parent) {
|
||||
const { id } = parent
|
||||
return await getPendingStreamRequests(id)
|
||||
}
|
||||
},
|
||||
Project: {
|
||||
async pendingAccessRequests(parent) {
|
||||
const { id } = parent
|
||||
return await getPendingProjectRequests(id)
|
||||
}
|
||||
},
|
||||
StreamAccessRequest: {
|
||||
async requester(parent, _args, ctx) {
|
||||
const { requesterId } = parent
|
||||
|
@ -77,6 +125,45 @@ const resolvers: Resolvers = {
|
|||
|
||||
return stream
|
||||
}
|
||||
},
|
||||
ProjectAccessRequest: {
|
||||
async requester(parent, _args, ctx) {
|
||||
const { requesterId } = parent
|
||||
const user = await ctx.loaders.users.getUser.load(requesterId)
|
||||
if (!user) {
|
||||
throw new LogicError('Unable to find requester')
|
||||
}
|
||||
|
||||
return user
|
||||
},
|
||||
async projectId(parent) {
|
||||
const { resourceId, resourceType } = parent
|
||||
if (resourceType !== AccessRequestType.Stream) {
|
||||
throw new LogicError('Unexpectedly returned invalid resource type')
|
||||
}
|
||||
|
||||
return resourceId
|
||||
},
|
||||
async project(parent, _args, ctx) {
|
||||
const { resourceId, resourceType } = parent
|
||||
if (resourceType !== AccessRequestType.Stream) {
|
||||
throw new LogicError('Unexpectedly returned invalid resource type')
|
||||
}
|
||||
|
||||
const project = await ctx.loaders.streams.getStream.load(resourceId)
|
||||
if (!project) {
|
||||
throw new LogicError('Unable to find request project')
|
||||
}
|
||||
|
||||
await validateStreamAccess(
|
||||
ctx.userId,
|
||||
project.id,
|
||||
Roles.Stream.Reviewer,
|
||||
ctx.resourceAccessRules
|
||||
)
|
||||
|
||||
return project
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { StreamAccessRequestRecord } from '@/modules/accessrequests/repositories'
|
||||
import { StreamAccessRequest } from '@/modules/core/graph/generated/graphql'
|
||||
|
||||
export type StreamAccessRequestGraphQLReturn = Omit<
|
||||
StreamAccessRequest,
|
||||
'requester' | 'stream'
|
||||
>
|
||||
|
||||
export type ProjectAccessRequestGraphQLReturn = StreamAccessRequestRecord
|
||||
|
|
|
@ -12,7 +12,8 @@ import {
|
|||
getPendingAccessRequest,
|
||||
getPendingAccessRequests,
|
||||
getUsersPendingAccessRequest,
|
||||
ServerAccessRequestRecord
|
||||
ServerAccessRequestRecord,
|
||||
StreamAccessRequestRecord
|
||||
} from '@/modules/accessrequests/repositories'
|
||||
import { StreamInvalidAccessError } from '@/modules/core/errors/stream'
|
||||
import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types'
|
||||
|
@ -36,44 +37,52 @@ function buildStreamAccessRequestGraphQLReturn(
|
|||
}
|
||||
}
|
||||
|
||||
export async function getUserProjectAccessRequest(
|
||||
userId: string,
|
||||
projectId: string
|
||||
): Promise<Nullable<StreamAccessRequestRecord>> {
|
||||
const req = await getUsersPendingAccessRequest(
|
||||
userId,
|
||||
AccessRequestType.Stream,
|
||||
projectId
|
||||
)
|
||||
return req || null
|
||||
}
|
||||
|
||||
export async function getUserStreamAccessRequest(
|
||||
userId: string,
|
||||
streamId: string
|
||||
): Promise<Nullable<StreamAccessRequestGraphQLReturn>> {
|
||||
const req = await getUsersPendingAccessRequest(
|
||||
userId,
|
||||
AccessRequestType.Stream,
|
||||
streamId
|
||||
)
|
||||
const req = await getUserProjectAccessRequest(userId, streamId)
|
||||
if (!req) return null
|
||||
|
||||
return buildStreamAccessRequestGraphQLReturn(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new stream access request
|
||||
* Create new project access request
|
||||
*/
|
||||
export async function requestStreamAccess(userId: string, streamId: string) {
|
||||
export async function requestProjectAccess(userId: string, projectId: string) {
|
||||
const [stream, existingRequest] = await Promise.all([
|
||||
getStream({ userId, streamId }),
|
||||
getUserStreamAccessRequest(userId, streamId)
|
||||
getStream({ userId, streamId: projectId }),
|
||||
getUserStreamAccessRequest(userId, projectId)
|
||||
])
|
||||
|
||||
if (existingRequest) {
|
||||
throw new AccessRequestCreationError(
|
||||
'User already has a pending access request for this stream'
|
||||
'User already has a pending access request for this resource'
|
||||
)
|
||||
}
|
||||
|
||||
if (!stream) {
|
||||
throw new AccessRequestCreationError(
|
||||
"Can't request access to a non-existant stream"
|
||||
"Can't request access to a non-existant resource"
|
||||
)
|
||||
}
|
||||
|
||||
if (stream.role) {
|
||||
throw new AccessRequestCreationError(
|
||||
'User already has access to the specified stream'
|
||||
'User already has access to the specified resource'
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -81,23 +90,40 @@ export async function requestStreamAccess(userId: string, streamId: string) {
|
|||
id: generateId(),
|
||||
requesterId: userId,
|
||||
resourceType: AccessRequestType.Stream,
|
||||
resourceId: streamId
|
||||
resourceId: projectId
|
||||
})
|
||||
|
||||
await AccessRequestsEmitter.emit(AccessRequestsEmitter.events.Created, {
|
||||
request: req
|
||||
})
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new stream access request
|
||||
*/
|
||||
export async function requestStreamAccess(userId: string, streamId: string) {
|
||||
const req = await requestProjectAccess(userId, streamId)
|
||||
return buildStreamAccessRequestGraphQLReturn(req)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pending project access requests
|
||||
*/
|
||||
export async function getPendingProjectRequests(
|
||||
projectId: string
|
||||
): Promise<StreamAccessRequestRecord[]> {
|
||||
return await getPendingAccessRequests(AccessRequestType.Stream, projectId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pending stream access requests
|
||||
*/
|
||||
export async function getPendingStreamRequests(
|
||||
streamId: string
|
||||
): Promise<StreamAccessRequestGraphQLReturn[]> {
|
||||
const reqs = await getPendingAccessRequests(AccessRequestType.Stream, streamId)
|
||||
const reqs = await getPendingProjectRequests(streamId)
|
||||
return reqs.map(buildStreamAccessRequestGraphQLReturn)
|
||||
}
|
||||
|
||||
|
@ -152,4 +178,8 @@ export async function processPendingStreamRequest(
|
|||
approved: accept ? { role } : undefined,
|
||||
finalizedBy: userId
|
||||
})
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
export const processPendingProjectRequest = processPendingStreamRequest
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
import {
|
||||
deleteRequestById,
|
||||
getPendingAccessRequest
|
||||
} from '@/modules/accessrequests/repositories'
|
||||
import { requestProjectAccess } from '@/modules/accessrequests/services/stream'
|
||||
import { ActionTypes } from '@/modules/activitystream/helpers/types'
|
||||
import {
|
||||
ServerAccessRequests,
|
||||
StreamActivity,
|
||||
Streams,
|
||||
Users
|
||||
} from '@/modules/core/dbSchema'
|
||||
import { StreamAccessUpdateError } from '@/modules/core/errors/stream'
|
||||
import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import { getStreamCollaborators } from '@/modules/core/repositories/streams'
|
||||
import {
|
||||
addOrUpdateStreamCollaborator,
|
||||
removeStreamCollaborator
|
||||
} from '@/modules/core/services/streams/streamAccessService'
|
||||
import { NotificationType } from '@/modules/notifications/helpers/types'
|
||||
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import {
|
||||
CreateProjectAccessRequestDocument,
|
||||
GetActiveUserFullProjectAccessRequestDocument,
|
||||
GetActiveUserProjectAccessRequestDocument,
|
||||
GetPendingProjectAccessRequestsDocument,
|
||||
StreamRole,
|
||||
UseProjectAccessRequestDocument
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import { testApolloServer, TestApolloServer } from '@/test/graphqlHelper'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import { EmailSendingServiceMock } from '@/test/mocks/global'
|
||||
import {
|
||||
buildNotificationsStateTracker,
|
||||
NotificationsStateManager
|
||||
} from '@/test/notificationsHelper'
|
||||
import { getStreamActivities } from '@/test/speckle-helpers/activityStreamHelper'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { expect } from 'chai'
|
||||
import { noop } from 'lodash'
|
||||
|
||||
const isNotCollaboratorError = (e: unknown) =>
|
||||
e instanceof StreamAccessUpdateError &&
|
||||
e.message.includes('User is not a stream collaborator')
|
||||
|
||||
const createReqAndGetId = async (userId: string, streamId: string) => {
|
||||
const createReqRes = await requestProjectAccess(userId, streamId)
|
||||
return createReqRes.id
|
||||
}
|
||||
|
||||
const cleanup = async () => {
|
||||
await truncateTables([Streams.name, ServerAccessRequests.name, Users.name])
|
||||
}
|
||||
|
||||
describe('Project access requests', () => {
|
||||
let apollo: TestApolloServer
|
||||
let notificationsStateManager: NotificationsStateManager
|
||||
|
||||
const me: BasicTestUser = {
|
||||
name: 'hello itsa me',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherGuy: BasicTestUser = {
|
||||
name: 'and im the other guy, hi!',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const anotherGuy: BasicTestUser = {
|
||||
name: 'and im another guy lol',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherGuysPrivateStream: BasicTestStream = {
|
||||
name: 'other guys test stream #1',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherGuysPublicStream: BasicTestStream = {
|
||||
name: 'other guys public test stream #2',
|
||||
isPublic: true,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const myPrivateStream: BasicTestStream = {
|
||||
name: 'this is my private stream #1',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await cleanup()
|
||||
await createTestUsers([me, otherGuy, anotherGuy])
|
||||
await createTestStreams([
|
||||
[otherGuysPrivateStream, otherGuy],
|
||||
[otherGuysPublicStream, otherGuy],
|
||||
[myPrivateStream, me]
|
||||
])
|
||||
apollo = await testApolloServer({
|
||||
authUserId: me.id
|
||||
})
|
||||
notificationsStateManager = buildNotificationsStateTracker()
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
notificationsStateManager.destroy()
|
||||
})
|
||||
|
||||
const createReq = async (projectId: string) =>
|
||||
await apollo.execute(CreateProjectAccessRequestDocument, {
|
||||
projectId
|
||||
})
|
||||
|
||||
const getActiveUserReq = async (projectId: string) =>
|
||||
await apollo.execute(GetActiveUserProjectAccessRequestDocument, {
|
||||
projectId
|
||||
})
|
||||
|
||||
const getPendingProjectReqs = async (projectId: string) =>
|
||||
await apollo.execute(GetPendingProjectAccessRequestsDocument, {
|
||||
projectId
|
||||
})
|
||||
|
||||
const getFullActiveUserAccessRequest = async (projectId: string) =>
|
||||
await apollo.execute(GetActiveUserFullProjectAccessRequestDocument, {
|
||||
projectId
|
||||
})
|
||||
|
||||
const useReq = async (
|
||||
requestId: string,
|
||||
accept: boolean,
|
||||
role: StreamRole = StreamRole.StreamContributor
|
||||
) =>
|
||||
await apollo.execute(UseProjectAccessRequestDocument, {
|
||||
requestId,
|
||||
accept,
|
||||
role
|
||||
})
|
||||
|
||||
describe('when being created', () => {
|
||||
beforeEach(async () => {
|
||||
await truncateTables([ServerAccessRequests.name, StreamActivity.name])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Ensure me doesnt have any roles on stream1
|
||||
await removeStreamCollaborator(otherGuysPrivateStream.id, me.id, me.id).catch(
|
||||
(e) => {
|
||||
if (!isNotCollaboratorError(e)) throw e
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('operation succeeds', async () => {
|
||||
const sendEmailCall = EmailSendingServiceMock.hijackFunction(
|
||||
'sendEmail',
|
||||
async () => true
|
||||
)
|
||||
|
||||
const waitForAck = notificationsStateManager.waitForAck(
|
||||
(e) => e.result?.type === NotificationType.NewStreamAccessRequest
|
||||
)
|
||||
|
||||
const results = await createReq(otherGuysPrivateStream.id)
|
||||
|
||||
// req gets created
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.projectMutations.accessRequestMutations.create.id).to.be.ok
|
||||
expect(results.data?.projectMutations.accessRequestMutations.create.createdAt).to
|
||||
.be.ok
|
||||
expect(results.data?.projectMutations.accessRequestMutations.create.requesterId)
|
||||
.to.be.ok
|
||||
expect(
|
||||
results.data?.projectMutations.accessRequestMutations.create.requester.id
|
||||
).to.eq(results.data?.projectMutations.accessRequestMutations.create.requesterId)
|
||||
expect(results.data?.projectMutations.accessRequestMutations.create.projectId).to
|
||||
.be.ok
|
||||
|
||||
await waitForAck
|
||||
|
||||
// email gets sent out
|
||||
expect(sendEmailCall.args?.[0]?.[0]).to.be.ok
|
||||
const emailParams = sendEmailCall.args[0][0]
|
||||
|
||||
expect(emailParams.subject).to.contain('A user requested access to your project')
|
||||
expect(emailParams.html).to.be.ok
|
||||
expect(emailParams.text).to.be.ok
|
||||
expect(emailParams.to).to.eq(otherGuy.email)
|
||||
|
||||
// activity stream item inserted
|
||||
const streamActivity = await getStreamActivities(otherGuysPrivateStream.id, {
|
||||
actionType: ActionTypes.Stream.AccessRequestSent,
|
||||
userId: me.id
|
||||
})
|
||||
expect(streamActivity).to.have.lengthOf(1)
|
||||
})
|
||||
|
||||
it('operation fails if request already exists', async () => {
|
||||
const firstResults = await createReq(otherGuysPrivateStream.id)
|
||||
expect(firstResults).to.not.haveGraphQLErrors()
|
||||
expect(firstResults.data?.projectMutations.accessRequestMutations.create.id).to.be
|
||||
.ok
|
||||
|
||||
const secondResults = await createReq(otherGuysPrivateStream.id)
|
||||
expect(secondResults).to.haveGraphQLErrors('already has a pending access request')
|
||||
expect(secondResults.data?.projectMutations.accessRequestMutations.create.id).to
|
||||
.be.not.ok
|
||||
})
|
||||
|
||||
it('operation fails if stream is nonexistant', async () => {
|
||||
const secondResults = await createReq('abcdef123')
|
||||
expect(secondResults).to.haveGraphQLErrors('non-existant resource')
|
||||
expect(secondResults.data?.projectMutations.accessRequestMutations.create.id).to
|
||||
.be.not.ok
|
||||
})
|
||||
|
||||
it('operation fails if user already has a role on the stream', async () => {
|
||||
await addOrUpdateStreamCollaborator(
|
||||
otherGuysPrivateStream.id,
|
||||
me.id,
|
||||
Roles.Stream.Contributor,
|
||||
otherGuy.id
|
||||
)
|
||||
|
||||
const secondResults = await createReq(otherGuysPrivateStream.id)
|
||||
expect(secondResults).to.haveGraphQLErrors('user already has access')
|
||||
expect(secondResults.data?.projectMutations.accessRequestMutations.create.id).to
|
||||
.be.not.ok
|
||||
})
|
||||
})
|
||||
|
||||
describe('when being read', () => {
|
||||
let myRequestId: string
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let myPublicReqId: string
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let anotherGuysRequestId: string
|
||||
|
||||
beforeEach(async () => {
|
||||
await truncateTables([ServerAccessRequests.name])
|
||||
|
||||
const [myNewReqId, anotherGuysNewReqId, myNewPublicReqId] = await Promise.all([
|
||||
createReqAndGetId(me.id, otherGuysPrivateStream.id),
|
||||
createReqAndGetId(anotherGuy.id, otherGuysPrivateStream.id),
|
||||
createReqAndGetId(me.id, otherGuysPublicStream.id)
|
||||
])
|
||||
myRequestId = myNewReqId
|
||||
anotherGuysRequestId = anotherGuysNewReqId
|
||||
myPublicReqId = myNewPublicReqId
|
||||
})
|
||||
|
||||
it('returns the request correctly', async () => {
|
||||
const results = await getActiveUserReq(otherGuysPrivateStream.id)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.activeUser?.projectAccessRequest?.id).to.eq(myRequestId)
|
||||
expect(results.data?.activeUser?.projectAccessRequest?.createdAt).to.be.ok
|
||||
expect(results.data?.activeUser?.projectAccessRequest?.requesterId).to.be.ok
|
||||
expect(results.data?.activeUser?.projectAccessRequest?.requester.id).to.eq(
|
||||
results.data?.activeUser?.projectAccessRequest?.requesterId
|
||||
)
|
||||
expect(results.data?.activeUser?.projectAccessRequest?.projectId).to.be.ok
|
||||
})
|
||||
|
||||
it('returns null if no req found', async () => {
|
||||
await deleteRequestById(myRequestId)
|
||||
|
||||
const results = await getActiveUserReq(otherGuysPrivateStream.id)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.activeUser?.projectAccessRequest).to.eq(null)
|
||||
})
|
||||
|
||||
it('throws error if attempting to read private stream metadata before has access to it', async () => {
|
||||
const results = await getFullActiveUserAccessRequest(otherGuysPrivateStream.id)
|
||||
|
||||
expect(results).to.haveGraphQLErrors(
|
||||
'User does not have required access to stream'
|
||||
)
|
||||
})
|
||||
|
||||
it('doesnt throw if attempting to read stream metadata on accessible stream', async () => {
|
||||
const results = await getFullActiveUserAccessRequest(otherGuysPublicStream.id)
|
||||
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.activeUser?.projectAccessRequest?.project.id).to.be.ok
|
||||
})
|
||||
})
|
||||
|
||||
describe('when being read from a stream', () => {
|
||||
before(async () => {
|
||||
await truncateTables([ServerAccessRequests.name])
|
||||
|
||||
await addOrUpdateStreamCollaborator(
|
||||
otherGuysPublicStream.id,
|
||||
me.id,
|
||||
Roles.Stream.Contributor,
|
||||
otherGuy.id
|
||||
)
|
||||
await Promise.all([
|
||||
createReqAndGetId(otherGuy.id, myPrivateStream.id),
|
||||
createReqAndGetId(anotherGuy.id, myPrivateStream.id)
|
||||
])
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await removeStreamCollaborator(otherGuysPublicStream.id, me.id, me.id).catch(noop)
|
||||
})
|
||||
|
||||
it(`operation fails if reading from a non-owned stream`, async () => {
|
||||
const results = await getPendingProjectReqs(otherGuysPublicStream.id)
|
||||
expect(results).to.haveGraphQLErrors('not authorized')
|
||||
expect(results.data?.project?.pendingAccessRequests).to.be.not.ok
|
||||
expect(results.data?.project?.id).to.be.ok
|
||||
})
|
||||
|
||||
it('operation succeeds', async () => {
|
||||
const results = await getPendingProjectReqs(myPrivateStream.id)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
|
||||
expect(results.data?.project?.pendingAccessRequests).to.have.lengthOf(2)
|
||||
|
||||
for (const pendingReq of results.data!.project!.pendingAccessRequests!) {
|
||||
expect(pendingReq.id).to.be.ok
|
||||
expect(pendingReq.createdAt).to.be.ok
|
||||
expect(pendingReq.requesterId).to.be.ok
|
||||
expect(pendingReq.projectId).to.be.ok
|
||||
expect(pendingReq.project.id).to.eq(results.data!.project!.id)
|
||||
expect(pendingReq.requester.id).to.eq(pendingReq.requesterId)
|
||||
expect([otherGuy.id, anotherGuy.id].includes(pendingReq.requesterId)).to.be.true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('when being processed', () => {
|
||||
let validReqId: string
|
||||
|
||||
beforeEach(async () => {
|
||||
await truncateTables([ServerAccessRequests.name, StreamActivity.name])
|
||||
await removeStreamCollaborator(
|
||||
myPrivateStream.id,
|
||||
otherGuy.id,
|
||||
otherGuy.id
|
||||
).catch((e) => {
|
||||
if (!isNotCollaboratorError(e)) throw e
|
||||
})
|
||||
validReqId = await createReqAndGetId(otherGuy.id, myPrivateStream.id)
|
||||
})
|
||||
|
||||
it('processing fails when pointing to nonexistant req', async () => {
|
||||
const results = await useReq('abcd', true)
|
||||
expect(results).to.haveGraphQLErrors('no request with this id exists')
|
||||
expect(results.data?.projectMutations.accessRequestMutations.use).to.be.not.ok
|
||||
})
|
||||
|
||||
it('processing fails when pointing to a req the user doesnt have access to', async () => {
|
||||
const inaccessibleReqId = await createReqAndGetId(
|
||||
anotherGuy.id,
|
||||
otherGuysPrivateStream.id
|
||||
)
|
||||
|
||||
const results = await useReq(inaccessibleReqId, true)
|
||||
expect(results).to.haveGraphQLErrors('you must own the stream')
|
||||
expect(results.data?.projectMutations.accessRequestMutations.use).to.be.not.ok
|
||||
})
|
||||
|
||||
const validProcessingDataSet = [
|
||||
{ display: 'declining', accept: false },
|
||||
{ display: 'approving', accept: true },
|
||||
{
|
||||
display: 'approving with custom role',
|
||||
accept: true,
|
||||
role: StreamRole.StreamReviewer
|
||||
}
|
||||
]
|
||||
validProcessingDataSet.forEach(({ display, accept, role }) => {
|
||||
it(`${display} works`, async () => {
|
||||
const results = await useReq(validReqId, accept, role)
|
||||
expect(results).to.not.haveGraphQLErrors()
|
||||
expect(results.data?.projectMutations.accessRequestMutations.use).to.be.ok
|
||||
|
||||
// req should be deleted
|
||||
const req = await getPendingAccessRequest(validReqId)
|
||||
expect(req).to.not.be.ok
|
||||
|
||||
// activity stream item should be inserted
|
||||
if (accept) {
|
||||
const streamActivity = await getStreamActivities(myPrivateStream.id, {
|
||||
actionType: ActionTypes.Stream.PermissionsAdd,
|
||||
userId: me.id
|
||||
})
|
||||
expect(streamActivity).to.have.lengthOf(1)
|
||||
|
||||
const collaborators = await getStreamCollaborators(myPrivateStream.id)
|
||||
const newCollaborator = collaborators.find((c) => c.id === otherGuy.id)
|
||||
|
||||
expect(newCollaborator).to.be.ok
|
||||
expect(newCollaborator?.streamRole).to.eq(
|
||||
role ? mapStreamRoleToValue(role) : Roles.Stream.Contributor
|
||||
)
|
||||
} else {
|
||||
const streamActivity = await getStreamActivities(myPrivateStream.id, {
|
||||
actionType: ActionTypes.Stream.AccessRequestDeclined,
|
||||
userId: me.id
|
||||
})
|
||||
expect(streamActivity).to.have.lengthOf(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -194,7 +194,7 @@ describe('Stream access requests', () => {
|
|||
|
||||
it('operation fails if stream is nonexistant', async () => {
|
||||
const secondResults = await createReq('abcdef123')
|
||||
expect(secondResults).to.haveGraphQLErrors('non-existant stream')
|
||||
expect(secondResults).to.haveGraphQLErrors('non-existant resource')
|
||||
expect(secondResults.data?.streamAccessRequestCreate.id).to.be.not.ok
|
||||
})
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ const { getStream } = require('@/modules/core/services/streams')
|
|||
const { Roles } = require('@/modules/core/helpers/mainConstants')
|
||||
|
||||
const {
|
||||
getComment,
|
||||
getComments,
|
||||
getResourceCommentCount,
|
||||
createComment,
|
||||
|
@ -15,6 +14,7 @@ const {
|
|||
editComment,
|
||||
streamResourceCheck
|
||||
} = require('@/modules/comments/services/index')
|
||||
const { getComment } = require('@/modules/comments/repositories/comments')
|
||||
const {
|
||||
ensureCommentSchema
|
||||
} = require('@/modules/comments/services/commentTextService')
|
||||
|
@ -62,19 +62,27 @@ const {
|
|||
convertLegacyDataToState
|
||||
} = require('@/modules/comments/services/data')
|
||||
|
||||
const getStreamComment = async ({ streamId, commentId }, ctx) => {
|
||||
await authorizeProjectCommentsAccess({
|
||||
projectId: streamId,
|
||||
authCtx: ctx
|
||||
})
|
||||
|
||||
const comment = await getComment({ id: commentId, userId: ctx.userId })
|
||||
if (comment.streamId !== streamId)
|
||||
throw new ApolloForbiddenError('You do not have access to this comment.')
|
||||
|
||||
return comment
|
||||
}
|
||||
|
||||
/** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */
|
||||
module.exports = {
|
||||
Query: {
|
||||
async comment(parent, args, context) {
|
||||
await authorizeProjectCommentsAccess({
|
||||
projectId: args.streamId,
|
||||
authCtx: context
|
||||
})
|
||||
|
||||
const comment = await getComment({ id: args.id, userId: context.userId })
|
||||
if (comment.streamId !== args.streamId)
|
||||
throw new ApolloForbiddenError('You do not have access to this comment.')
|
||||
return comment
|
||||
async comment(_parent, args, context) {
|
||||
return await getStreamComment(
|
||||
{ streamId: args.streamId, commentId: args.id },
|
||||
context
|
||||
)
|
||||
},
|
||||
|
||||
async comments(parent, args, context) {
|
||||
|
@ -210,6 +218,12 @@ module.exports = {
|
|||
threadsOnly: true
|
||||
}
|
||||
})
|
||||
},
|
||||
async comment(parent, args, context) {
|
||||
return await getStreamComment(
|
||||
{ streamId: parent.id, commentId: args.id },
|
||||
context
|
||||
)
|
||||
}
|
||||
},
|
||||
Version: {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import {
|
||||
CreateCommentInput,
|
||||
CreateProjectCommentDocument
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import { testApolloServer, TestApolloServer } from '@/test/graphqlHelper'
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import {
|
||||
BasicTestBranch,
|
||||
createTestBranches
|
||||
} from '@/test/speckle-helpers/branchHelper'
|
||||
import { BasicTestCommit, createTestCommits } from '@/test/speckle-helpers/commitHelper'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { SpeckleViewer } from '@speckle/shared'
|
||||
import { RichTextEditor } from '@speckle/shared'
|
||||
import { expect } from 'chai'
|
||||
|
||||
const resourceUrlBuilder = SpeckleViewer.ViewerRoute.resourceBuilder
|
||||
|
||||
describe('Project Comments', () => {
|
||||
const me: BasicTestUser = {
|
||||
name: 'hello itsa me',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
const myStream: BasicTestStream = {
|
||||
name: 'this is my great stream #1',
|
||||
isPublic: true,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
const myBranch: BasicTestBranch = {
|
||||
name: 'nice branch!!',
|
||||
streamId: '',
|
||||
id: '',
|
||||
authorId: ''
|
||||
}
|
||||
const myCommit: BasicTestCommit = {
|
||||
id: '',
|
||||
objectId: '',
|
||||
streamId: '',
|
||||
authorId: '',
|
||||
message: 'this is my nice commit :)))',
|
||||
branchName: myBranch.name
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await beforeEachContext()
|
||||
await createTestUsers([me])
|
||||
await createTestStreams([[myStream, me]])
|
||||
await createTestBranches([{ branch: myBranch, stream: myStream, owner: me }])
|
||||
await createTestCommits([myCommit], { stream: myStream, owner: me })
|
||||
})
|
||||
|
||||
describe('in GraphQL API', () => {
|
||||
let apollo: TestApolloServer
|
||||
|
||||
before(async () => {
|
||||
apollo = await testApolloServer({
|
||||
authUserId: me.id
|
||||
})
|
||||
})
|
||||
|
||||
const createProjectComment = async (input: CreateCommentInput) =>
|
||||
await apollo.execute(CreateProjectCommentDocument, { input })
|
||||
|
||||
it('can be created', async () => {
|
||||
const input: CreateCommentInput = {
|
||||
projectId: myStream.id,
|
||||
resourceIdString: resourceUrlBuilder()
|
||||
.addModel(myBranch.id, myCommit.id)
|
||||
.toString(),
|
||||
content: {
|
||||
doc: RichTextEditor.convertBasicStringToDocument('hello world')
|
||||
}
|
||||
}
|
||||
|
||||
const res = await createProjectComment(input)
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.commentMutations.create.id).to.be.ok
|
||||
expect(res.data?.commentMutations.create.rawText).to.equal('hello world')
|
||||
expect(res.data?.commentMutations.create.text.doc).to.be.ok
|
||||
expect(res.data?.commentMutations.create.authorId).to.equal(me.id)
|
||||
})
|
||||
|
||||
describe('after creation', () => {
|
||||
it.skip('can be retrieved through Project.comment')
|
||||
})
|
||||
})
|
||||
})
|
|
@ -15,6 +15,11 @@ export class CommitCreateError extends BaseError {
|
|||
static code = 'COMMIT_CREATE_ERROR'
|
||||
}
|
||||
|
||||
export class CommitReceiveError extends BaseError {
|
||||
static defaultMessage = 'An issue occurred while receiving a commit'
|
||||
static code = 'COMMIT_RECEIVE_ERROR'
|
||||
}
|
||||
|
||||
export class CommitUpdateError extends BaseError {
|
||||
static defaultMessage = 'An issue occurred while updating a commit'
|
||||
static code = 'COMMIT_UPDATE_ERROR'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
|
||||
import { StreamGraphQLReturn, CommitGraphQLReturn, ProjectGraphQLReturn, VersionGraphQLReturn, ServerInviteGraphQLReturnType, ModelGraphQLReturn, ModelsTreeItemGraphQLReturn, LimitedUserGraphQLReturn, MutationsObjectGraphQLReturn, GraphQLEmptyReturn } from '@/modules/core/helpers/graphTypes';
|
||||
import { StreamAccessRequestGraphQLReturn } from '@/modules/accessrequests/helpers/graphTypes';
|
||||
import { StreamGraphQLReturn, CommitGraphQLReturn, ProjectGraphQLReturn, ObjectGraphQLReturn, VersionGraphQLReturn, ServerInviteGraphQLReturnType, ModelGraphQLReturn, ModelsTreeItemGraphQLReturn, MutationsObjectGraphQLReturn, LimitedUserGraphQLReturn, GraphQLEmptyReturn } from '@/modules/core/helpers/graphTypes';
|
||||
import { StreamAccessRequestGraphQLReturn, ProjectAccessRequestGraphQLReturn } from '@/modules/accessrequests/helpers/graphTypes';
|
||||
import { CommentReplyAuthorCollectionGraphQLReturn, CommentGraphQLReturn } from '@/modules/comments/helpers/graphTypes';
|
||||
import { PendingStreamCollaboratorGraphQLReturn } from '@/modules/serverinvites/helpers/graphTypes';
|
||||
import { FileUploadGraphQLReturn } from '@/modules/fileuploads/helpers/types';
|
||||
|
@ -26,7 +26,6 @@ export type Scalars = {
|
|||
BigInt: { input: bigint; output: bigint; }
|
||||
/** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */
|
||||
DateTime: { input: Date; output: Date; }
|
||||
EmailAddress: { input: any; output: any; }
|
||||
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
||||
JSONObject: { input: Record<string, unknown>; output: Record<string, unknown>; }
|
||||
};
|
||||
|
@ -811,6 +810,16 @@ export type CreateModelInput = {
|
|||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type CreateVersionInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
modelId: Scalars['String']['input'];
|
||||
objectId: Scalars['String']['input'];
|
||||
parents?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
projectId: Scalars['String']['input'];
|
||||
sourceApplication?: InputMaybe<Scalars['String']['input']>;
|
||||
totalChildrenCount?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
export type DeleteModelInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
|
@ -1001,6 +1010,13 @@ export type LimitedUserTimelineArgs = {
|
|||
limit?: Scalars['Int']['input'];
|
||||
};
|
||||
|
||||
export type MarkReceivedVersionInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
projectId: Scalars['String']['input'];
|
||||
sourceApplication: Scalars['String']['input'];
|
||||
versionId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type Model = {
|
||||
__typename?: 'Model';
|
||||
author: LimitedUser;
|
||||
|
@ -1170,11 +1186,11 @@ export type Mutation = {
|
|||
* @deprecated Use commentMutations version
|
||||
*/
|
||||
commentView: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.create instead. */
|
||||
commitCreate: Scalars['String']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.delete instead. */
|
||||
commitDelete: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.markReceived instead. */
|
||||
commitReceive: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.update/moveToModel instead. */
|
||||
commitUpdate: Scalars['Boolean']['output'];
|
||||
|
@ -1200,7 +1216,7 @@ export type Mutation = {
|
|||
inviteResend: Scalars['Boolean']['output'];
|
||||
modelMutations: ModelMutations;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
objectCreate: Array<Maybe<Scalars['String']['output']>>;
|
||||
objectCreate: Array<Scalars['String']['output']>;
|
||||
projectMutations: ProjectMutations;
|
||||
/** (Re-)send the account verification e-mail */
|
||||
requestVerification: Scalars['Boolean']['output'];
|
||||
|
@ -1212,12 +1228,12 @@ export type Mutation = {
|
|||
serverInviteCreate: Scalars['Boolean']['output'];
|
||||
/**
|
||||
* Request access to a specific stream
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.create instead.
|
||||
*/
|
||||
streamAccessRequestCreate: StreamAccessRequest;
|
||||
/**
|
||||
* Accept or decline a stream access request. Must be a stream owner to invoke this.
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.use instead.
|
||||
*/
|
||||
streamAccessRequestUse: Scalars['Boolean']['output'];
|
||||
/**
|
||||
|
@ -1274,7 +1290,7 @@ export type Mutation = {
|
|||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectMutations.updateRole instead.
|
||||
*/
|
||||
streamUpdatePermission?: Maybe<Scalars['Boolean']['output']>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use ProjectMutations.batchDelete instead. */
|
||||
streamsDelete: Scalars['Boolean']['output'];
|
||||
/**
|
||||
* Used for broadcasting real time typing status in comment threads. Does not persist any info.
|
||||
|
@ -1591,6 +1607,7 @@ export type MutationWebhookUpdateArgs = {
|
|||
|
||||
export type Object = {
|
||||
__typename?: 'Object';
|
||||
/** @deprecated Not implemented. */
|
||||
applicationId?: Maybe<Scalars['String']['output']>;
|
||||
/**
|
||||
* Get any objects that this object references. In the case of commits, this will give you a commit's constituent objects.
|
||||
|
@ -1630,7 +1647,7 @@ export type ObjectChildrenArgs = {
|
|||
export type ObjectCollection = {
|
||||
__typename?: 'ObjectCollection';
|
||||
cursor?: Maybe<Scalars['String']['output']>;
|
||||
objects: Array<Maybe<Object>>;
|
||||
objects: Array<Object>;
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
|
@ -1691,6 +1708,8 @@ export type Project = {
|
|||
blob?: Maybe<BlobMetadata>;
|
||||
/** Get the metadata collection of blobs stored for this stream. */
|
||||
blobs?: Maybe<BlobMetadataCollection>;
|
||||
/** Get specific project comment/thread by ID */
|
||||
comment?: Maybe<Comment>;
|
||||
/** All comment threads in this project */
|
||||
commentThreads: ProjectCommentCollection;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
|
@ -1700,6 +1719,8 @@ export type Project = {
|
|||
invitedTeam?: Maybe<Array<PendingStreamCollaborator>>;
|
||||
/** Returns a specific model by its ID */
|
||||
model: Model;
|
||||
/** Retrieve a specific project model by its ID */
|
||||
modelByName: Model;
|
||||
/** Return a model tree of children for the specified model name */
|
||||
modelChildrenTree: Array<ModelsTreeItem>;
|
||||
/** Returns a flat list of all models */
|
||||
|
@ -1710,6 +1731,9 @@ export type Project = {
|
|||
*/
|
||||
modelsTree: ModelsTreeItemCollection;
|
||||
name: Scalars['String']['output'];
|
||||
object?: Maybe<Object>;
|
||||
/** Pending project access requests */
|
||||
pendingAccessRequests?: Maybe<Array<ProjectAccessRequest>>;
|
||||
/** Returns a list models that are being created from a file import */
|
||||
pendingImportedModels: Array<FileUpload>;
|
||||
/** Active user's role for this project. `null` if request is not authenticated, or the project is not explicitly shared with you. */
|
||||
|
@ -1753,6 +1777,11 @@ export type ProjectBlobsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectCommentArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectCommentThreadsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']['input']>;
|
||||
filter?: InputMaybe<ProjectCommentsFilter>;
|
||||
|
@ -1765,6 +1794,11 @@ export type ProjectModelArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectModelByNameArgs = {
|
||||
name: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectModelChildrenTreeArgs = {
|
||||
fullName: Scalars['String']['input'];
|
||||
};
|
||||
|
@ -1784,6 +1818,11 @@ export type ProjectModelsTreeArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectObjectArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectPendingImportedModelsArgs = {
|
||||
limit?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
@ -1810,6 +1849,38 @@ export type ProjectWebhooksArgs = {
|
|||
id?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
/** Created when a user requests to become a contributor on a project */
|
||||
export type ProjectAccessRequest = {
|
||||
__typename?: 'ProjectAccessRequest';
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
/** Can only be selected if authed user has proper access */
|
||||
project: Project;
|
||||
projectId: Scalars['String']['output'];
|
||||
requester: LimitedUser;
|
||||
requesterId: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type ProjectAccessRequestMutations = {
|
||||
__typename?: 'ProjectAccessRequestMutations';
|
||||
/** Request access to a specific project */
|
||||
create: ProjectAccessRequest;
|
||||
/** Accept or decline a project access request. Must be a project owner to invoke this. */
|
||||
use: Project;
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccessRequestMutationsCreateArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccessRequestMutationsUseArgs = {
|
||||
accept: Scalars['Boolean']['input'];
|
||||
requestId: Scalars['String']['input'];
|
||||
role?: StreamRole;
|
||||
};
|
||||
|
||||
export type ProjectAutomationCreateInput = {
|
||||
enabled: Scalars['Boolean']['input'];
|
||||
name: Scalars['String']['input'];
|
||||
|
@ -2052,7 +2123,11 @@ export enum ProjectModelsUpdatedMessageType {
|
|||
|
||||
export type ProjectMutations = {
|
||||
__typename?: 'ProjectMutations';
|
||||
/** Access request related mutations */
|
||||
accessRequestMutations: ProjectAccessRequestMutations;
|
||||
automationMutations: ProjectAutomationMutations;
|
||||
/** Batch delete projects */
|
||||
batchDelete: Scalars['Boolean']['output'];
|
||||
/** Create new project */
|
||||
create: Project;
|
||||
/**
|
||||
|
@ -2078,6 +2153,11 @@ export type ProjectMutationsAutomationMutationsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectMutationsBatchDeleteArgs = {
|
||||
ids: Array<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
|
||||
export type ProjectMutationsCreateArgs = {
|
||||
input?: InputMaybe<ProjectCreateInput>;
|
||||
};
|
||||
|
@ -2241,7 +2321,7 @@ export type Query = {
|
|||
automateFunctions: AutomateFunctionCollection;
|
||||
/** Part of the automation/function creation handshake mechanism */
|
||||
automateValidateAuthCode: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.comment instead. */
|
||||
comment?: Maybe<Comment>;
|
||||
/**
|
||||
* This query can be used in the following ways:
|
||||
|
@ -2280,7 +2360,7 @@ export type Query = {
|
|||
stream?: Maybe<Stream>;
|
||||
/**
|
||||
* Get authed user's stream access request
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use User.projectAccessRequest instead.
|
||||
*/
|
||||
streamAccessRequest?: Maybe<StreamAccessRequest>;
|
||||
/**
|
||||
|
@ -2645,7 +2725,7 @@ export type Stream = {
|
|||
* @deprecated Part of the old API surface and will be removed in the future. Use Project.blobs instead.
|
||||
*/
|
||||
blobs?: Maybe<BlobMetadataCollection>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.model instead. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.model or Project.modelByName instead. */
|
||||
branch?: Maybe<Branch>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.models or Project.modelsTree instead. */
|
||||
branches?: Maybe<BranchCollection>;
|
||||
|
@ -2690,11 +2770,11 @@ export type Stream = {
|
|||
/** Whether the stream can be viewed by non-contributors */
|
||||
isPublic: Scalars['Boolean']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.object instead. */
|
||||
object?: Maybe<Object>;
|
||||
/**
|
||||
* Pending stream access requests
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use Project.pendingAccessRequests instead.
|
||||
*/
|
||||
pendingAccessRequests?: Maybe<Array<StreamAccessRequest>>;
|
||||
/** Collaborators who have been invited, but not yet accepted. */
|
||||
|
@ -3198,6 +3278,8 @@ export type User = {
|
|||
name: Scalars['String']['output'];
|
||||
notificationPreferences: Scalars['JSONObject']['output'];
|
||||
profiles?: Maybe<Scalars['JSONObject']['output']>;
|
||||
/** Get pending project access request, that the user made */
|
||||
projectAccessRequest?: Maybe<ProjectAccessRequest>;
|
||||
/** Get all invitations to projects that the active user has */
|
||||
projectInvites: Array<PendingStreamCollaborator>;
|
||||
/** Get projects that the user participates in */
|
||||
|
@ -3263,6 +3345,15 @@ export type UserFavoriteStreamsArgs = {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Full user type, should only be used in the context of admin operations or
|
||||
* when a user is reading/writing info about himself
|
||||
*/
|
||||
export type UserProjectAccessRequestArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Full user type, should only be used in the context of admin operations or
|
||||
* when a user is reading/writing info about himself
|
||||
|
@ -3407,18 +3498,30 @@ export type VersionCreatedTriggerDefinition = {
|
|||
|
||||
export type VersionMutations = {
|
||||
__typename?: 'VersionMutations';
|
||||
create: Version;
|
||||
delete: Scalars['Boolean']['output'];
|
||||
markReceived: Scalars['Boolean']['output'];
|
||||
moveToModel: Model;
|
||||
requestGendoAIRender: Scalars['Boolean']['output'];
|
||||
update: Version;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsCreateArgs = {
|
||||
input: CreateVersionInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsDeleteArgs = {
|
||||
input: DeleteVersionsInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsMarkReceivedArgs = {
|
||||
input: MarkReceivedVersionInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsMoveToModelArgs = {
|
||||
input: MoveVersionsInput;
|
||||
};
|
||||
|
@ -3700,13 +3803,13 @@ export type ResolversTypes = {
|
|||
CreateCommentInput: CreateCommentInput;
|
||||
CreateCommentReplyInput: CreateCommentReplyInput;
|
||||
CreateModelInput: CreateModelInput;
|
||||
CreateVersionInput: CreateVersionInput;
|
||||
DateTime: ResolverTypeWrapper<Scalars['DateTime']['output']>;
|
||||
DeleteModelInput: DeleteModelInput;
|
||||
DeleteVersionsInput: DeleteVersionsInput;
|
||||
DiscoverableStreamsSortType: DiscoverableStreamsSortType;
|
||||
DiscoverableStreamsSortingInput: DiscoverableStreamsSortingInput;
|
||||
EditCommentInput: EditCommentInput;
|
||||
EmailAddress: ResolverTypeWrapper<Scalars['EmailAddress']['output']>;
|
||||
FileUpload: ResolverTypeWrapper<FileUploadGraphQLReturn>;
|
||||
Float: ResolverTypeWrapper<Scalars['Float']['output']>;
|
||||
GendoAIRender: ResolverTypeWrapper<GendoAiRender>;
|
||||
|
@ -3717,6 +3820,7 @@ export type ResolversTypes = {
|
|||
JSONObject: ResolverTypeWrapper<Scalars['JSONObject']['output']>;
|
||||
LegacyCommentViewerData: ResolverTypeWrapper<LegacyCommentViewerData>;
|
||||
LimitedUser: ResolverTypeWrapper<LimitedUserGraphQLReturn>;
|
||||
MarkReceivedVersionInput: MarkReceivedVersionInput;
|
||||
Model: ResolverTypeWrapper<ModelGraphQLReturn>;
|
||||
ModelCollection: ResolverTypeWrapper<Omit<ModelCollection, 'items'> & { items: Array<ResolversTypes['Model']> }>;
|
||||
ModelMutations: ResolverTypeWrapper<MutationsObjectGraphQLReturn>;
|
||||
|
@ -3725,13 +3829,15 @@ export type ResolversTypes = {
|
|||
ModelsTreeItemCollection: ResolverTypeWrapper<Omit<ModelsTreeItemCollection, 'items'> & { items: Array<ResolversTypes['ModelsTreeItem']> }>;
|
||||
MoveVersionsInput: MoveVersionsInput;
|
||||
Mutation: ResolverTypeWrapper<{}>;
|
||||
Object: ResolverTypeWrapper<Object>;
|
||||
ObjectCollection: ResolverTypeWrapper<ObjectCollection>;
|
||||
Object: ResolverTypeWrapper<ObjectGraphQLReturn>;
|
||||
ObjectCollection: ResolverTypeWrapper<Omit<ObjectCollection, 'objects'> & { objects: Array<ResolversTypes['Object']> }>;
|
||||
ObjectCreateInput: ObjectCreateInput;
|
||||
PasswordStrengthCheckFeedback: ResolverTypeWrapper<PasswordStrengthCheckFeedback>;
|
||||
PasswordStrengthCheckResults: ResolverTypeWrapper<PasswordStrengthCheckResults>;
|
||||
PendingStreamCollaborator: ResolverTypeWrapper<PendingStreamCollaboratorGraphQLReturn>;
|
||||
Project: ResolverTypeWrapper<ProjectGraphQLReturn>;
|
||||
ProjectAccessRequest: ResolverTypeWrapper<ProjectAccessRequestGraphQLReturn>;
|
||||
ProjectAccessRequestMutations: ResolverTypeWrapper<MutationsObjectGraphQLReturn>;
|
||||
ProjectAutomationCreateInput: ProjectAutomationCreateInput;
|
||||
ProjectAutomationMutations: ResolverTypeWrapper<ProjectAutomationMutationsGraphQLReturn>;
|
||||
ProjectAutomationRevisionCreateInput: ProjectAutomationRevisionCreateInput;
|
||||
|
@ -3813,7 +3919,7 @@ export type ResolversTypes = {
|
|||
UpdateAutomateFunctionInput: UpdateAutomateFunctionInput;
|
||||
UpdateModelInput: UpdateModelInput;
|
||||
UpdateVersionInput: UpdateVersionInput;
|
||||
User: ResolverTypeWrapper<Omit<User, 'automateInfo' | 'commits' | 'favoriteStreams' | 'projectInvites' | 'projects' | 'streams'> & { automateInfo: ResolversTypes['UserAutomateInfo'], commits?: Maybe<ResolversTypes['CommitCollection']>, favoriteStreams: ResolversTypes['StreamCollection'], projectInvites: Array<ResolversTypes['PendingStreamCollaborator']>, projects: ResolversTypes['ProjectCollection'], streams: ResolversTypes['StreamCollection'] }>;
|
||||
User: ResolverTypeWrapper<Omit<User, 'automateInfo' | 'commits' | 'favoriteStreams' | 'projectAccessRequest' | 'projectInvites' | 'projects' | 'streams'> & { automateInfo: ResolversTypes['UserAutomateInfo'], commits?: Maybe<ResolversTypes['CommitCollection']>, favoriteStreams: ResolversTypes['StreamCollection'], projectAccessRequest?: Maybe<ResolversTypes['ProjectAccessRequest']>, projectInvites: Array<ResolversTypes['PendingStreamCollaborator']>, projects: ResolversTypes['ProjectCollection'], streams: ResolversTypes['StreamCollection'] }>;
|
||||
UserAutomateInfo: ResolverTypeWrapper<UserAutomateInfoGraphQLReturn>;
|
||||
UserDeleteInput: UserDeleteInput;
|
||||
UserProjectsFilter: UserProjectsFilter;
|
||||
|
@ -3915,12 +4021,12 @@ export type ResolversParentTypes = {
|
|||
CreateCommentInput: CreateCommentInput;
|
||||
CreateCommentReplyInput: CreateCommentReplyInput;
|
||||
CreateModelInput: CreateModelInput;
|
||||
CreateVersionInput: CreateVersionInput;
|
||||
DateTime: Scalars['DateTime']['output'];
|
||||
DeleteModelInput: DeleteModelInput;
|
||||
DeleteVersionsInput: DeleteVersionsInput;
|
||||
DiscoverableStreamsSortingInput: DiscoverableStreamsSortingInput;
|
||||
EditCommentInput: EditCommentInput;
|
||||
EmailAddress: Scalars['EmailAddress']['output'];
|
||||
FileUpload: FileUploadGraphQLReturn;
|
||||
Float: Scalars['Float']['output'];
|
||||
GendoAIRender: GendoAiRender;
|
||||
|
@ -3931,6 +4037,7 @@ export type ResolversParentTypes = {
|
|||
JSONObject: Scalars['JSONObject']['output'];
|
||||
LegacyCommentViewerData: LegacyCommentViewerData;
|
||||
LimitedUser: LimitedUserGraphQLReturn;
|
||||
MarkReceivedVersionInput: MarkReceivedVersionInput;
|
||||
Model: ModelGraphQLReturn;
|
||||
ModelCollection: Omit<ModelCollection, 'items'> & { items: Array<ResolversParentTypes['Model']> };
|
||||
ModelMutations: MutationsObjectGraphQLReturn;
|
||||
|
@ -3939,13 +4046,15 @@ export type ResolversParentTypes = {
|
|||
ModelsTreeItemCollection: Omit<ModelsTreeItemCollection, 'items'> & { items: Array<ResolversParentTypes['ModelsTreeItem']> };
|
||||
MoveVersionsInput: MoveVersionsInput;
|
||||
Mutation: {};
|
||||
Object: Object;
|
||||
ObjectCollection: ObjectCollection;
|
||||
Object: ObjectGraphQLReturn;
|
||||
ObjectCollection: Omit<ObjectCollection, 'objects'> & { objects: Array<ResolversParentTypes['Object']> };
|
||||
ObjectCreateInput: ObjectCreateInput;
|
||||
PasswordStrengthCheckFeedback: PasswordStrengthCheckFeedback;
|
||||
PasswordStrengthCheckResults: PasswordStrengthCheckResults;
|
||||
PendingStreamCollaborator: PendingStreamCollaboratorGraphQLReturn;
|
||||
Project: ProjectGraphQLReturn;
|
||||
ProjectAccessRequest: ProjectAccessRequestGraphQLReturn;
|
||||
ProjectAccessRequestMutations: MutationsObjectGraphQLReturn;
|
||||
ProjectAutomationCreateInput: ProjectAutomationCreateInput;
|
||||
ProjectAutomationMutations: ProjectAutomationMutationsGraphQLReturn;
|
||||
ProjectAutomationRevisionCreateInput: ProjectAutomationRevisionCreateInput;
|
||||
|
@ -4012,7 +4121,7 @@ export type ResolversParentTypes = {
|
|||
UpdateAutomateFunctionInput: UpdateAutomateFunctionInput;
|
||||
UpdateModelInput: UpdateModelInput;
|
||||
UpdateVersionInput: UpdateVersionInput;
|
||||
User: Omit<User, 'automateInfo' | 'commits' | 'favoriteStreams' | 'projectInvites' | 'projects' | 'streams'> & { automateInfo: ResolversParentTypes['UserAutomateInfo'], commits?: Maybe<ResolversParentTypes['CommitCollection']>, favoriteStreams: ResolversParentTypes['StreamCollection'], projectInvites: Array<ResolversParentTypes['PendingStreamCollaborator']>, projects: ResolversParentTypes['ProjectCollection'], streams: ResolversParentTypes['StreamCollection'] };
|
||||
User: Omit<User, 'automateInfo' | 'commits' | 'favoriteStreams' | 'projectAccessRequest' | 'projectInvites' | 'projects' | 'streams'> & { automateInfo: ResolversParentTypes['UserAutomateInfo'], commits?: Maybe<ResolversParentTypes['CommitCollection']>, favoriteStreams: ResolversParentTypes['StreamCollection'], projectAccessRequest?: Maybe<ResolversParentTypes['ProjectAccessRequest']>, projectInvites: Array<ResolversParentTypes['PendingStreamCollaborator']>, projects: ResolversParentTypes['ProjectCollection'], streams: ResolversParentTypes['StreamCollection'] };
|
||||
UserAutomateInfo: UserAutomateInfoGraphQLReturn;
|
||||
UserDeleteInput: UserDeleteInput;
|
||||
UserProjectsFilter: UserProjectsFilter;
|
||||
|
@ -4459,10 +4568,6 @@ export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig<ResolversT
|
|||
name: 'DateTime';
|
||||
}
|
||||
|
||||
export interface EmailAddressScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['EmailAddress'], any> {
|
||||
name: 'EmailAddress';
|
||||
}
|
||||
|
||||
export type FileUploadResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['FileUpload'] = ResolversParentTypes['FileUpload']> = {
|
||||
branchName?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
convertedCommitId?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
|
@ -4618,7 +4723,7 @@ export type MutationResolvers<ContextType = GraphQLContext, ParentType extends R
|
|||
inviteDelete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationInviteDeleteArgs, 'inviteId'>>;
|
||||
inviteResend?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationInviteResendArgs, 'inviteId'>>;
|
||||
modelMutations?: Resolver<ResolversTypes['ModelMutations'], ParentType, ContextType>;
|
||||
objectCreate?: Resolver<Array<Maybe<ResolversTypes['String']>>, ParentType, ContextType, RequireFields<MutationObjectCreateArgs, 'objectInput'>>;
|
||||
objectCreate?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType, RequireFields<MutationObjectCreateArgs, 'objectInput'>>;
|
||||
projectMutations?: Resolver<ResolversTypes['ProjectMutations'], ParentType, ContextType>;
|
||||
requestVerification?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
requestVerificationByEmail?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationRequestVerificationByEmailArgs, 'email'>>;
|
||||
|
@ -4665,7 +4770,7 @@ export type ObjectResolvers<ContextType = GraphQLContext, ParentType extends Res
|
|||
|
||||
export type ObjectCollectionResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ObjectCollection'] = ResolversParentTypes['ObjectCollection']> = {
|
||||
cursor?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
objects?: Resolver<Array<Maybe<ResolversTypes['Object']>>, ParentType, ContextType>;
|
||||
objects?: Resolver<Array<ResolversTypes['Object']>, ParentType, ContextType>;
|
||||
totalCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
@ -4703,16 +4808,20 @@ export type ProjectResolvers<ContextType = GraphQLContext, ParentType extends Re
|
|||
automations?: Resolver<ResolversTypes['AutomationCollection'], ParentType, ContextType, Partial<ProjectAutomationsArgs>>;
|
||||
blob?: Resolver<Maybe<ResolversTypes['BlobMetadata']>, ParentType, ContextType, RequireFields<ProjectBlobArgs, 'id'>>;
|
||||
blobs?: Resolver<Maybe<ResolversTypes['BlobMetadataCollection']>, ParentType, ContextType, RequireFields<ProjectBlobsArgs, 'cursor' | 'limit' | 'query'>>;
|
||||
comment?: Resolver<Maybe<ResolversTypes['Comment']>, ParentType, ContextType, RequireFields<ProjectCommentArgs, 'id'>>;
|
||||
commentThreads?: Resolver<ResolversTypes['ProjectCommentCollection'], ParentType, ContextType, RequireFields<ProjectCommentThreadsArgs, 'limit'>>;
|
||||
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
invitedTeam?: Resolver<Maybe<Array<ResolversTypes['PendingStreamCollaborator']>>, ParentType, ContextType>;
|
||||
model?: Resolver<ResolversTypes['Model'], ParentType, ContextType, RequireFields<ProjectModelArgs, 'id'>>;
|
||||
modelByName?: Resolver<ResolversTypes['Model'], ParentType, ContextType, RequireFields<ProjectModelByNameArgs, 'name'>>;
|
||||
modelChildrenTree?: Resolver<Array<ResolversTypes['ModelsTreeItem']>, ParentType, ContextType, RequireFields<ProjectModelChildrenTreeArgs, 'fullName'>>;
|
||||
models?: Resolver<ResolversTypes['ModelCollection'], ParentType, ContextType, RequireFields<ProjectModelsArgs, 'limit'>>;
|
||||
modelsTree?: Resolver<ResolversTypes['ModelsTreeItemCollection'], ParentType, ContextType, RequireFields<ProjectModelsTreeArgs, 'limit'>>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
object?: Resolver<Maybe<ResolversTypes['Object']>, ParentType, ContextType, RequireFields<ProjectObjectArgs, 'id'>>;
|
||||
pendingAccessRequests?: Resolver<Maybe<Array<ResolversTypes['ProjectAccessRequest']>>, ParentType, ContextType>;
|
||||
pendingImportedModels?: Resolver<Array<ResolversTypes['FileUpload']>, ParentType, ContextType, RequireFields<ProjectPendingImportedModelsArgs, 'limit'>>;
|
||||
role?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
sourceApps?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
|
@ -4726,6 +4835,22 @@ export type ProjectResolvers<ContextType = GraphQLContext, ParentType extends Re
|
|||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ProjectAccessRequestResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ProjectAccessRequest'] = ResolversParentTypes['ProjectAccessRequest']> = {
|
||||
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
project?: Resolver<ResolversTypes['Project'], ParentType, ContextType>;
|
||||
projectId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
requester?: Resolver<ResolversTypes['LimitedUser'], ParentType, ContextType>;
|
||||
requesterId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ProjectAccessRequestMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ProjectAccessRequestMutations'] = ResolversParentTypes['ProjectAccessRequestMutations']> = {
|
||||
create?: Resolver<ResolversTypes['ProjectAccessRequest'], ParentType, ContextType, RequireFields<ProjectAccessRequestMutationsCreateArgs, 'projectId'>>;
|
||||
use?: Resolver<ResolversTypes['Project'], ParentType, ContextType, RequireFields<ProjectAccessRequestMutationsUseArgs, 'accept' | 'requestId' | 'role'>>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ProjectAutomationMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ProjectAutomationMutations'] = ResolversParentTypes['ProjectAutomationMutations']> = {
|
||||
create?: Resolver<ResolversTypes['Automation'], ParentType, ContextType, RequireFields<ProjectAutomationMutationsCreateArgs, 'input'>>;
|
||||
createRevision?: Resolver<ResolversTypes['AutomationRevision'], ParentType, ContextType, RequireFields<ProjectAutomationMutationsCreateRevisionArgs, 'input'>>;
|
||||
|
@ -4796,7 +4921,9 @@ export type ProjectModelsUpdatedMessageResolvers<ContextType = GraphQLContext, P
|
|||
};
|
||||
|
||||
export type ProjectMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ProjectMutations'] = ResolversParentTypes['ProjectMutations']> = {
|
||||
accessRequestMutations?: Resolver<ResolversTypes['ProjectAccessRequestMutations'], ParentType, ContextType>;
|
||||
automationMutations?: Resolver<ResolversTypes['ProjectAutomationMutations'], ParentType, ContextType, RequireFields<ProjectMutationsAutomationMutationsArgs, 'projectId'>>;
|
||||
batchDelete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<ProjectMutationsBatchDeleteArgs, 'ids'>>;
|
||||
create?: Resolver<ResolversTypes['Project'], ParentType, ContextType, Partial<ProjectMutationsCreateArgs>>;
|
||||
createForOnboarding?: Resolver<ResolversTypes['Project'], ParentType, ContextType>;
|
||||
delete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<ProjectMutationsDeleteArgs, 'id'>>;
|
||||
|
@ -5144,6 +5271,7 @@ export type UserResolvers<ContextType = GraphQLContext, ParentType extends Resol
|
|||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
notificationPreferences?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
|
||||
profiles?: Resolver<Maybe<ResolversTypes['JSONObject']>, ParentType, ContextType>;
|
||||
projectAccessRequest?: Resolver<Maybe<ResolversTypes['ProjectAccessRequest']>, ParentType, ContextType, RequireFields<UserProjectAccessRequestArgs, 'projectId'>>;
|
||||
projectInvites?: Resolver<Array<ResolversTypes['PendingStreamCollaborator']>, ParentType, ContextType>;
|
||||
projects?: Resolver<ResolversTypes['ProjectCollection'], ParentType, ContextType, RequireFields<UserProjectsArgs, 'limit'>>;
|
||||
role?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
|
@ -5213,7 +5341,9 @@ export type VersionCreatedTriggerDefinitionResolvers<ContextType = GraphQLContex
|
|||
};
|
||||
|
||||
export type VersionMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['VersionMutations'] = ResolversParentTypes['VersionMutations']> = {
|
||||
create?: Resolver<ResolversTypes['Version'], ParentType, ContextType, RequireFields<VersionMutationsCreateArgs, 'input'>>;
|
||||
delete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<VersionMutationsDeleteArgs, 'input'>>;
|
||||
markReceived?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<VersionMutationsMarkReceivedArgs, 'input'>>;
|
||||
moveToModel?: Resolver<ResolversTypes['Model'], ParentType, ContextType, RequireFields<VersionMutationsMoveToModelArgs, 'input'>>;
|
||||
requestGendoAIRender?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<VersionMutationsRequestGendoAiRenderArgs, 'input'>>;
|
||||
update?: Resolver<ResolversTypes['Version'], ParentType, ContextType, RequireFields<VersionMutationsUpdateArgs, 'input'>>;
|
||||
|
@ -5325,7 +5455,6 @@ export type Resolvers<ContextType = GraphQLContext> = {
|
|||
CommitCollection?: CommitCollectionResolvers<ContextType>;
|
||||
CountOnlyCollection?: CountOnlyCollectionResolvers<ContextType>;
|
||||
DateTime?: GraphQLScalarType;
|
||||
EmailAddress?: GraphQLScalarType;
|
||||
FileUpload?: FileUploadResolvers<ContextType>;
|
||||
GendoAIRender?: GendoAiRenderResolvers<ContextType>;
|
||||
GendoAIRenderCollection?: GendoAiRenderCollectionResolvers<ContextType>;
|
||||
|
@ -5344,6 +5473,8 @@ export type Resolvers<ContextType = GraphQLContext> = {
|
|||
PasswordStrengthCheckResults?: PasswordStrengthCheckResultsResolvers<ContextType>;
|
||||
PendingStreamCollaborator?: PendingStreamCollaboratorResolvers<ContextType>;
|
||||
Project?: ProjectResolvers<ContextType>;
|
||||
ProjectAccessRequest?: ProjectAccessRequestResolvers<ContextType>;
|
||||
ProjectAccessRequestMutations?: ProjectAccessRequestMutationsResolvers<ContextType>;
|
||||
ProjectAutomationMutations?: ProjectAutomationMutationsResolvers<ContextType>;
|
||||
ProjectAutomationsUpdatedMessage?: ProjectAutomationsUpdatedMessageResolvers<ContextType>;
|
||||
ProjectCollaborator?: ProjectCollaboratorResolvers<ContextType>;
|
||||
|
|
|
@ -22,13 +22,9 @@ const {
|
|||
const {
|
||||
createCommitByBranchName,
|
||||
updateCommitAndNotify,
|
||||
deleteCommitAndNotify
|
||||
deleteCommitAndNotify,
|
||||
markCommitReceivedAndNotify
|
||||
} = require('@/modules/core/services/commit/management')
|
||||
const {
|
||||
addCommitReceivedActivity
|
||||
} = require('@/modules/activitystream/services/commitActivity')
|
||||
|
||||
const { getUser } = require('../../services/users')
|
||||
|
||||
const { RateLimitError } = require('@/modules/core/errors/ratelimit')
|
||||
const {
|
||||
|
@ -231,18 +227,12 @@ module.exports = {
|
|||
context.resourceAccessRules
|
||||
)
|
||||
|
||||
const commit = await getCommitById({
|
||||
streamId: args.input.streamId,
|
||||
id: args.input.commitId
|
||||
await markCommitReceivedAndNotify({
|
||||
input: args.input,
|
||||
userId: context.userId
|
||||
})
|
||||
const user = await getUser(context.userId)
|
||||
|
||||
if (commit && user) {
|
||||
await addCommitReceivedActivity({ input: args.input, userId: user.id })
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
},
|
||||
|
||||
async commitDelete(_parent, args, context) {
|
||||
|
|
|
@ -58,6 +58,16 @@ export = {
|
|||
|
||||
return model
|
||||
},
|
||||
async modelByName(parent, args, ctx) {
|
||||
const model = await ctx.loaders.streams.getStreamBranchByName
|
||||
.forStream(parent.id)
|
||||
.load(args.name)
|
||||
if (!model) {
|
||||
throw new BranchNotFoundError('Model not found')
|
||||
}
|
||||
|
||||
return model
|
||||
},
|
||||
async modelsTree(parent, args) {
|
||||
return await getProjectTopLevelModelsTree(parent.id, args)
|
||||
},
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
'use strict'
|
||||
const { validateScopes, authorizeResolver } = require('@/modules/shared')
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
const {
|
||||
import {
|
||||
createObjects,
|
||||
getObject,
|
||||
getObjectChildren,
|
||||
getObjectChildrenQuery
|
||||
} = require('../../services/objects')
|
||||
const { Roles, Scopes } = require('@speckle/shared')
|
||||
const { throwForNotHavingServerRole } = require('@/modules/shared/authz')
|
||||
} from '../../services/objects'
|
||||
|
||||
module.exports = {
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
import { getObject } from '@/modules/core/repositories/objects'
|
||||
|
||||
const getStreamObject: NonNullable<Resolvers['Stream']>['object'] =
|
||||
async function object(parent, args) {
|
||||
return (await getObject(args.id, parent.id)) || null
|
||||
}
|
||||
|
||||
export = {
|
||||
Stream: {
|
||||
async object(parent, args) {
|
||||
const obj = await getObject({ streamId: parent.id, objectId: args.id })
|
||||
if (!obj) return null
|
||||
|
||||
obj.streamId = parent.id
|
||||
return obj
|
||||
}
|
||||
object: getStreamObject
|
||||
},
|
||||
Project: {
|
||||
object: getStreamObject
|
||||
},
|
||||
Object: {
|
||||
async children(parent, args) {
|
||||
|
@ -34,7 +36,7 @@ module.exports = {
|
|||
})
|
||||
result.objects.forEach((x) => (x.streamId = parent.streamId))
|
||||
return {
|
||||
totalCount: parent.totalChildrenCount || '0',
|
||||
totalCount: parent.totalChildrenCount || 0,
|
||||
cursor: result.cursor,
|
||||
objects: result.objects
|
||||
}
|
||||
|
@ -56,9 +58,7 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
Mutation: {
|
||||
async objectCreate(parent, args, context) {
|
||||
await throwForNotHavingServerRole(context, Roles.Server.Guest)
|
||||
await validateScopes(context.scopes, Scopes.Streams.Write)
|
||||
async objectCreate(_parent, args, context) {
|
||||
await authorizeResolver(
|
||||
context.userId,
|
||||
args.objectInput.streamId,
|
||||
|
@ -73,4 +73,4 @@ module.exports = {
|
|||
return ids
|
||||
}
|
||||
}
|
||||
}
|
||||
} as Resolvers
|
|
@ -89,6 +89,16 @@ export = {
|
|||
projectMutations: () => ({})
|
||||
},
|
||||
ProjectMutations: {
|
||||
async batchDelete(_parent, args, ctx) {
|
||||
const results = await Promise.all(
|
||||
args.ids.map((id) =>
|
||||
deleteStreamAndNotify(id, ctx.userId!, ctx.resourceAccessRules, {
|
||||
skipAccessChecks: true
|
||||
})
|
||||
)
|
||||
)
|
||||
return results.every((res) => res === true)
|
||||
},
|
||||
async delete(_parent, { id }, { userId, resourceAccessRules }) {
|
||||
return await deleteStreamAndNotify(id, userId!, resourceAccessRules)
|
||||
},
|
||||
|
|
|
@ -11,7 +11,16 @@ import {
|
|||
batchMoveCommits
|
||||
} from '@/modules/core/services/commit/batchCommitActions'
|
||||
import { CommitUpdateError } from '@/modules/core/errors/commit'
|
||||
import { updateCommitAndNotify } from '@/modules/core/services/commit/management'
|
||||
import {
|
||||
createCommitByBranchId,
|
||||
markCommitReceivedAndNotify,
|
||||
updateCommitAndNotify
|
||||
} from '@/modules/core/services/commit/management'
|
||||
import {
|
||||
getRateLimitResult,
|
||||
isRateLimitBreached
|
||||
} from '@/modules/core/services/ratelimiter'
|
||||
import { RateLimitError } from '@/modules/core/errors/ratelimit'
|
||||
|
||||
export = {
|
||||
Project: {
|
||||
|
@ -62,6 +71,47 @@ export = {
|
|||
ctx.resourceAccessRules
|
||||
)
|
||||
return await updateCommitAndNotify(args.input, ctx.userId!)
|
||||
},
|
||||
async create(_parent, args, ctx) {
|
||||
await authorizeResolver(
|
||||
ctx.userId,
|
||||
args.input.projectId,
|
||||
Roles.Stream.Contributor,
|
||||
ctx.resourceAccessRules
|
||||
)
|
||||
|
||||
const rateLimitResult = await getRateLimitResult('COMMIT_CREATE', ctx.userId!)
|
||||
if (isRateLimitBreached(rateLimitResult)) {
|
||||
throw new RateLimitError(rateLimitResult)
|
||||
}
|
||||
|
||||
const commit = await createCommitByBranchId({
|
||||
authorId: ctx.userId!,
|
||||
streamId: args.input.projectId,
|
||||
branchId: args.input.modelId,
|
||||
message: args.input.message || null,
|
||||
sourceApplication: args.input.sourceApplication || null,
|
||||
objectId: args.input.objectId,
|
||||
parents: args.input.parents || []
|
||||
})
|
||||
|
||||
return commit
|
||||
},
|
||||
|
||||
async markReceived(_parent, args, ctx) {
|
||||
await authorizeResolver(
|
||||
ctx.userId,
|
||||
args.input.projectId,
|
||||
Roles.Stream.Reviewer,
|
||||
ctx.resourceAccessRules
|
||||
)
|
||||
|
||||
await markCommitReceivedAndNotify({
|
||||
input: args.input,
|
||||
userId: ctx.userId!
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
},
|
||||
Subscription: {
|
||||
|
|
|
@ -13,8 +13,6 @@ exports.scalarResolvers = {
|
|||
exports.scalarSchemas = `
|
||||
scalar DateTime
|
||||
|
||||
scalar EmailAddress
|
||||
|
||||
scalar BigInt
|
||||
|
||||
scalar JSONObject
|
||||
|
|
|
@ -7,7 +7,12 @@ import {
|
|||
Commit
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { Roles, ServerRoles, StreamRoles } from '@/modules/core/helpers/mainConstants'
|
||||
import { BranchRecord, CommitRecord, StreamRecord } from '@/modules/core/helpers/types'
|
||||
import {
|
||||
BranchRecord,
|
||||
CommitRecord,
|
||||
ObjectRecord,
|
||||
StreamRecord
|
||||
} from '@/modules/core/helpers/types'
|
||||
import { Nullable } from '@speckle/shared'
|
||||
|
||||
/**
|
||||
|
@ -72,6 +77,8 @@ export type ModelsTreeItemGraphQLReturn = Omit<ModelsTreeItem, 'model' | 'childr
|
|||
projectId: string
|
||||
}
|
||||
|
||||
export type ObjectGraphQLReturn = ObjectRecord
|
||||
|
||||
/**
|
||||
* Return type for top-level mutations groupings like `projectMutations`, `activeUserMutations` etc.
|
||||
*/
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
import { TokenCreateError } from '@/modules/core/errors/user'
|
||||
import { TokenResourceAccessRecord } from '@/modules/core/helpers/types'
|
||||
import { ResourceTargets } from '@/modules/serverinvites/helpers/inviteHelper'
|
||||
import {} from '@/modules/serverinvites/services/operations'
|
||||
import { MaybeNullOrUndefined, Nullable, Optional, Scopes } from '@speckle/shared'
|
||||
import { differenceBy } from 'lodash'
|
||||
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import {
|
||||
addCommitCreatedActivity,
|
||||
addCommitDeletedActivity,
|
||||
addCommitReceivedActivity,
|
||||
addCommitUpdatedActivity
|
||||
} from '@/modules/activitystream/services/commitActivity'
|
||||
import {
|
||||
CommitCreateError,
|
||||
CommitDeleteError,
|
||||
CommitReceiveError,
|
||||
CommitUpdateError
|
||||
} from '@/modules/core/errors/commit'
|
||||
import { VersionEvents, VersionsEmitter } from '@/modules/core/events/versionsEmitter'
|
||||
import {
|
||||
CommitReceivedInput,
|
||||
CommitUpdateInput,
|
||||
MarkReceivedVersionInput,
|
||||
UpdateVersionInput
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { CommitRecord } from '@/modules/core/helpers/types'
|
||||
|
@ -38,9 +42,35 @@ import {
|
|||
import { ensureError, MaybeNullOrUndefined, Nullable, Roles } from '@speckle/shared'
|
||||
import { has } from 'lodash'
|
||||
|
||||
/**
|
||||
* Note: Doesn't notify subscriptions or save activityStream due to missing branchName
|
||||
*/
|
||||
export async function markCommitReceivedAndNotify(params: {
|
||||
input: MarkReceivedVersionInput | CommitReceivedInput
|
||||
userId: string
|
||||
}) {
|
||||
const { input, userId } = params
|
||||
|
||||
const oldInput: CommitReceivedInput =
|
||||
'projectId' in input
|
||||
? {
|
||||
...input,
|
||||
streamId: input.projectId,
|
||||
commitId: input.versionId
|
||||
}
|
||||
: input
|
||||
|
||||
const commit = await getCommit(oldInput.commitId, { streamId: oldInput.streamId })
|
||||
if (!commit) {
|
||||
throw new CommitReceiveError(
|
||||
`Failed to find commit with id ${oldInput.commitId} in stream ${oldInput.streamId}.`,
|
||||
{ info: params }
|
||||
)
|
||||
}
|
||||
|
||||
await addCommitReceivedActivity({
|
||||
input: oldInput,
|
||||
userId
|
||||
})
|
||||
}
|
||||
|
||||
export async function createCommitByBranchId(
|
||||
params: {
|
||||
streamId: string
|
||||
|
|
|
@ -169,6 +169,9 @@ module.exports = {
|
|||
return ids
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
async createObjects(streamId, objects) {
|
||||
// TODO: Switch to knex batch inserting functionality
|
||||
// see http://knexjs.org/#Utility-BatchInsert
|
||||
|
@ -248,6 +251,9 @@ module.exports = {
|
|||
return ids
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Promise<Omit<import('@/modules/core/helpers/types').ObjectRecord, 'streamId'>>}
|
||||
*/
|
||||
async getObject({ streamId, objectId }) {
|
||||
const res = await Objects().where({ streamId, id: objectId }).select('*').first()
|
||||
if (!res) return null
|
||||
|
@ -288,6 +294,9 @@ module.exports = {
|
|||
return q.stream({ highWaterMark: 500 })
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Promise<{objects: import('@/modules/core/helpers/types').ObjectRecord[], cursor: string | null}>}
|
||||
*/
|
||||
async getObjectChildren({ streamId, objectId, limit, depth, select, cursor }) {
|
||||
limit = parseInt(limit) || 50
|
||||
depth = parseInt(depth) || 1000
|
||||
|
@ -370,8 +379,13 @@ module.exports = {
|
|||
return { objects: rows, cursor: lastId }
|
||||
},
|
||||
|
||||
// This query is inefficient on larger sets (n * 10k objects) as we need to return the total count on an arbitrarily (user) defined selection of objects.
|
||||
// A possible future optimisation route would be to cache the total count of a query (as objects are immutable, it will not change) on a first run, and, if found on a subsequent round, do a simpler query and merge the total count result.
|
||||
/**
|
||||
*
|
||||
* This query is inefficient on larger sets (n * 10k objects) as we need to return the total count on an arbitrarily (user) defined selection of objects.
|
||||
* A possible future optimisation route would be to cache the total count of a query (as objects are immutable, it will not change) on a first run, and, if found on a subsequent round, do a simpler query and merge the total count result.
|
||||
* @param {*} param0
|
||||
* @returns {Promise<{objects: import('@/modules/core/helpers/types').ObjectRecord[], cursor: string | null, totalCount: number}>}
|
||||
*/
|
||||
async getObjectChildrenQuery({
|
||||
streamId,
|
||||
objectId,
|
||||
|
|
|
@ -41,7 +41,7 @@ async function isStreamCollaborator(userId, streamId) {
|
|||
* @param {string} [userId] If falsy, will throw for non-public streams
|
||||
* @param {string} streamId
|
||||
* @param {string} [expectedRole] Defaults to reviewer
|
||||
* @param {import('@/modules/serverinvites/services/operations').TokenResourceIdentifier[] | undefined | null} [userResourceAccessLimits]
|
||||
* @param {import('@/modules/core/domain/tokens/types').TokenResourceIdentifier[] | undefined | null} [userResourceAccessLimits]
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async function validateStreamAccess(
|
||||
|
@ -90,7 +90,7 @@ async function validateStreamAccess(
|
|||
* @param {string} streamId
|
||||
* @param {string} userId ID of user that should be removed
|
||||
* @param {string} removedById ID of user that is doing the removing
|
||||
* @param {import('@/modules/serverinvites/services/operations').TokenResourceIdentifier[] | undefined | null} [removerResourceAccessRules] Resource access rules (if any) for the user doing the removing
|
||||
* @param {import('@/modules/core/domain/tokens/types').TokenResourceIdentifier[] | undefined | null} [removerResourceAccessRules] Resource access rules (if any) for the user doing the removing
|
||||
*/
|
||||
async function removeStreamCollaborator(
|
||||
streamId,
|
||||
|
@ -134,7 +134,7 @@ async function removeStreamCollaborator(
|
|||
* @param {string} userId ID of user who is being added
|
||||
* @param {string} role
|
||||
* @param {string} addedById ID of user who is adding the new collaborator
|
||||
* @param {import('@/modules/serverinvites/services/operations').TokenResourceIdentifier[] | undefined | null} [adderResourceAccessRules] Resource access rules (if any) for the user doing the adding
|
||||
* @param {import('@/modules/core/domain/tokens/types').TokenResourceIdentifier[] | undefined | null} [adderResourceAccessRules] Resource access rules (if any) for the user doing the adding
|
||||
* @param {{
|
||||
* fromInvite?: boolean,
|
||||
* }} param4
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import {
|
||||
CreateModelInput,
|
||||
CreateProjectModelDocument,
|
||||
FindProjectModelByNameDocument
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import { testApolloServer, TestApolloServer } from '@/test/graphqlHelper'
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { expect } from 'chai'
|
||||
import { omit } from 'lodash'
|
||||
import { before, describe } from 'mocha'
|
||||
|
||||
describe('Models', () => {
|
||||
const me: BasicTestUser = {
|
||||
name: 'hello itsa me',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const myPrivateStream: BasicTestStream = {
|
||||
name: 'this is my private stream #1',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await beforeEachContext()
|
||||
await createTestUsers([me])
|
||||
await createTestStreams([[myPrivateStream, me]])
|
||||
})
|
||||
|
||||
describe('in GraphQL API', () => {
|
||||
let apollo: TestApolloServer
|
||||
|
||||
const createModel = async (input: CreateModelInput) =>
|
||||
await apollo.execute(CreateProjectModelDocument, {
|
||||
input
|
||||
})
|
||||
|
||||
before(async () => {
|
||||
apollo = await testApolloServer({
|
||||
authUserId: me.id
|
||||
})
|
||||
})
|
||||
|
||||
it('can be created', async () => {
|
||||
const input: CreateModelInput = {
|
||||
projectId: myPrivateStream.id,
|
||||
name: 'my first model',
|
||||
description: 'ayyooo'
|
||||
}
|
||||
const res = await createModel(input)
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.modelMutations.create.id).to.be.ok
|
||||
expect(res.data?.modelMutations.create.name).to.equal(input.name)
|
||||
expect(res.data?.modelMutations.create.description).to.equal(input.description)
|
||||
})
|
||||
|
||||
describe('after creation', () => {
|
||||
let firstModel: CreateModelInput & { id: string }
|
||||
|
||||
before(async () => {
|
||||
firstModel = {
|
||||
projectId: myPrivateStream.id,
|
||||
name: 'anutha model #1',
|
||||
description: 'ayyooo!!',
|
||||
id: ''
|
||||
}
|
||||
const res = await createModel(omit(firstModel, ['id']))
|
||||
firstModel.id = res.data!.modelMutations.create.id
|
||||
expect(firstModel.id).to.be.ok
|
||||
})
|
||||
|
||||
it('can be found by name', async () => {
|
||||
const res = await apollo.execute(FindProjectModelByNameDocument, {
|
||||
projectId: myPrivateStream.id,
|
||||
name: firstModel.name
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.project.modelByName.id).to.equal(firstModel.id)
|
||||
expect(res.data?.project.modelByName.name).to.equal(firstModel.name)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,132 @@
|
|||
import { before, describe } from 'mocha'
|
||||
import { expect } from 'chai'
|
||||
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { testApolloServer, TestApolloServer } from '@/test/graphqlHelper'
|
||||
import {
|
||||
BatchDeleteProjectsDocument,
|
||||
CreateProjectDocument,
|
||||
GetProjectObjectDocument,
|
||||
ProjectCreateInput
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import { createTestObject } from '@/test/speckle-helpers/commitHelper'
|
||||
import { times } from 'lodash'
|
||||
import { Roles } from '@speckle/shared'
|
||||
|
||||
describe('Projects', () => {
|
||||
const me: BasicTestUser = {
|
||||
name: 'hello itsa me',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const otherUser: BasicTestUser = {
|
||||
name: 'hello itsa some otha guy',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await beforeEachContext()
|
||||
await createTestUsers([me, otherUser])
|
||||
})
|
||||
|
||||
describe('in GraphQL API', () => {
|
||||
let apollo: TestApolloServer
|
||||
|
||||
before(async () => {
|
||||
apollo = await testApolloServer({
|
||||
authUserId: me.id
|
||||
})
|
||||
})
|
||||
|
||||
it('can be created', async () => {
|
||||
const input: ProjectCreateInput = {
|
||||
name: 'my first project',
|
||||
description: 'ayyooo'
|
||||
}
|
||||
const res = await apollo.execute(CreateProjectDocument, {
|
||||
input
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.projectMutations.create.id).to.be.ok
|
||||
expect(res.data?.projectMutations.create.name).to.equal(input.name)
|
||||
expect(res.data?.projectMutations.create.description).to.equal(input.description)
|
||||
})
|
||||
|
||||
describe('after creation', () => {
|
||||
const myStream: BasicTestStream = {
|
||||
name: 'this is my great stream #1',
|
||||
isPublic: true,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const getMyStreamObject = async (objectId: string) =>
|
||||
await apollo.execute(GetProjectObjectDocument, {
|
||||
projectId: myStream.id,
|
||||
objectId
|
||||
})
|
||||
|
||||
before(async () => {
|
||||
await createTestStreams([[myStream, me]])
|
||||
})
|
||||
|
||||
it('returns null if querying for a non-existant object()', async () => {
|
||||
const res = await getMyStreamObject('non-existant-object-id')
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.project.object).to.be.null
|
||||
})
|
||||
|
||||
it('can have their objects retrieved through object()', async () => {
|
||||
const objectId = await createTestObject({ projectId: myStream.id })
|
||||
const res = await getMyStreamObject(objectId)
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.project.object?.id).to.equal(objectId)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when doing batch deletion', () => {
|
||||
const createOtherGuyProjectBatch = async () => {
|
||||
const projects: BasicTestStream[] = times(3, () => ({
|
||||
id: '',
|
||||
ownerId: otherUser.id,
|
||||
name: `project ${Math.random()}`,
|
||||
isPublic: false
|
||||
}))
|
||||
|
||||
await createTestStreams(projects.map((p) => [p, me]))
|
||||
return projects.map((p) => p.id)
|
||||
}
|
||||
|
||||
const batchDeleteProjects = async (ids: string[], asAdmin?: boolean) =>
|
||||
await apollo.execute(
|
||||
BatchDeleteProjectsDocument,
|
||||
{ ids },
|
||||
{
|
||||
context: asAdmin ? { role: Roles.Server.Admin } : undefined
|
||||
}
|
||||
)
|
||||
|
||||
it("it doesn't work if user is not an admin", async () => {
|
||||
const projectIds = await createOtherGuyProjectBatch()
|
||||
const res = await batchDeleteProjects(projectIds)
|
||||
|
||||
expect(res.data).to.not.be.ok
|
||||
expect(res).to.haveGraphQLErrors('You do not have the required server role')
|
||||
})
|
||||
|
||||
it('works if user is an admin, even for not owned projects', async () => {
|
||||
const projectIds = await createOtherGuyProjectBatch()
|
||||
const res = await batchDeleteProjects(projectIds, true)
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.projectMutations.batchDelete).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,126 @@
|
|||
import { ActionTypes } from '@/modules/activitystream/helpers/types'
|
||||
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
|
||||
import {
|
||||
CreateProjectVersionDocument,
|
||||
CreateVersionInput,
|
||||
MarkProjectVersionReceivedDocument,
|
||||
MarkReceivedVersionInput
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import { testApolloServer, TestApolloServer } from '@/test/graphqlHelper'
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { getStreamActivities } from '@/test/speckle-helpers/activityStreamHelper'
|
||||
import {
|
||||
BasicTestBranch,
|
||||
createTestBranches
|
||||
} from '@/test/speckle-helpers/branchHelper'
|
||||
import { createTestObject } from '@/test/speckle-helpers/commitHelper'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { expect } from 'chai'
|
||||
import { omit } from 'lodash'
|
||||
import { before, describe } from 'mocha'
|
||||
|
||||
describe('Versions', () => {
|
||||
const me: BasicTestUser = {
|
||||
name: 'hello itsa me',
|
||||
email: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const myPrivateStream: BasicTestStream = {
|
||||
name: 'this is my private stream #1',
|
||||
isPublic: false,
|
||||
ownerId: '',
|
||||
id: ''
|
||||
}
|
||||
|
||||
const myBranch: BasicTestBranch = {
|
||||
name: 'my branchy #1',
|
||||
streamId: '',
|
||||
id: '',
|
||||
authorId: ''
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
await beforeEachContext()
|
||||
await createTestUsers([me])
|
||||
await createTestStreams([[myPrivateStream, me]])
|
||||
await createTestBranches([{ branch: myBranch, stream: myPrivateStream, owner: me }])
|
||||
})
|
||||
|
||||
describe('in GraphQL API', () => {
|
||||
let apollo: TestApolloServer
|
||||
let objectId: string
|
||||
|
||||
const createVersion = async (input: CreateVersionInput) =>
|
||||
await apollo.execute(CreateProjectVersionDocument, { input })
|
||||
|
||||
before(async () => {
|
||||
apollo = await testApolloServer({
|
||||
authUserId: me.id
|
||||
})
|
||||
objectId = await createTestObject({ projectId: myPrivateStream.id })
|
||||
})
|
||||
|
||||
it('can be created', async () => {
|
||||
const input: CreateVersionInput = {
|
||||
projectId: myPrivateStream.id,
|
||||
modelId: myBranch.id,
|
||||
objectId,
|
||||
message: 'Yoooo!',
|
||||
sourceApplication: 'tests',
|
||||
parents: []
|
||||
}
|
||||
const res = await createVersion(input)
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.versionMutations.create.id).to.be.ok
|
||||
expect(res.data?.versionMutations.create.message).to.eq(input.message)
|
||||
expect(res.data?.versionMutations.create.sourceApplication).to.eq(
|
||||
input.sourceApplication
|
||||
)
|
||||
expect(res.data?.versionMutations.create.model.id).to.eq(myBranch.id)
|
||||
expect(res.data?.versionMutations.create.referencedObject).to.eq(objectId)
|
||||
})
|
||||
|
||||
describe('after creation', () => {
|
||||
let firstVersion: CreateVersionInput & { id: string }
|
||||
|
||||
before(async () => {
|
||||
firstVersion = {
|
||||
projectId: myPrivateStream.id,
|
||||
modelId: myBranch.id,
|
||||
objectId,
|
||||
message: 'welcome #1',
|
||||
sourceApplication: 'testsz',
|
||||
parents: [],
|
||||
id: ''
|
||||
}
|
||||
const res = await createVersion(omit(firstVersion, ['id']))
|
||||
firstVersion.id = res.data!.versionMutations.create.id
|
||||
expect(firstVersion.id).to.be.ok
|
||||
})
|
||||
|
||||
it('can be marked as received', async () => {
|
||||
const input: MarkReceivedVersionInput = {
|
||||
versionId: firstVersion.id,
|
||||
projectId: myPrivateStream.id,
|
||||
sourceApplication: 'booo',
|
||||
message: 'hey hihihi'
|
||||
}
|
||||
const res = await apollo.execute(MarkProjectVersionReceivedDocument, {
|
||||
input
|
||||
})
|
||||
|
||||
expect(res).to.not.haveGraphQLErrors()
|
||||
expect(res.data?.versionMutations.markReceived).to.be.true
|
||||
|
||||
const activities = await getStreamActivities(myPrivateStream.id, {
|
||||
actionType: ActionTypes.Commit.Receive,
|
||||
userId: me.id
|
||||
})
|
||||
expect(activities).to.have.length(1)
|
||||
expect(activities[0].info?.message).to.eq(input.message)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -16,7 +16,6 @@ export type Scalars = {
|
|||
BigInt: { input: bigint; output: bigint; }
|
||||
/** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */
|
||||
DateTime: { input: string; output: string; }
|
||||
EmailAddress: { input: any; output: any; }
|
||||
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
||||
JSONObject: { input: Record<string, unknown>; output: Record<string, unknown>; }
|
||||
};
|
||||
|
@ -801,6 +800,16 @@ export type CreateModelInput = {
|
|||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type CreateVersionInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
modelId: Scalars['String']['input'];
|
||||
objectId: Scalars['String']['input'];
|
||||
parents?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
projectId: Scalars['String']['input'];
|
||||
sourceApplication?: InputMaybe<Scalars['String']['input']>;
|
||||
totalChildrenCount?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
export type DeleteModelInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
|
@ -991,6 +1000,13 @@ export type LimitedUserTimelineArgs = {
|
|||
limit?: Scalars['Int']['input'];
|
||||
};
|
||||
|
||||
export type MarkReceivedVersionInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
projectId: Scalars['String']['input'];
|
||||
sourceApplication: Scalars['String']['input'];
|
||||
versionId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type Model = {
|
||||
__typename?: 'Model';
|
||||
author: LimitedUser;
|
||||
|
@ -1160,11 +1176,11 @@ export type Mutation = {
|
|||
* @deprecated Use commentMutations version
|
||||
*/
|
||||
commentView: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.create instead. */
|
||||
commitCreate: Scalars['String']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.delete instead. */
|
||||
commitDelete: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.markReceived instead. */
|
||||
commitReceive: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.update/moveToModel instead. */
|
||||
commitUpdate: Scalars['Boolean']['output'];
|
||||
|
@ -1190,7 +1206,7 @@ export type Mutation = {
|
|||
inviteResend: Scalars['Boolean']['output'];
|
||||
modelMutations: ModelMutations;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
objectCreate: Array<Maybe<Scalars['String']['output']>>;
|
||||
objectCreate: Array<Scalars['String']['output']>;
|
||||
projectMutations: ProjectMutations;
|
||||
/** (Re-)send the account verification e-mail */
|
||||
requestVerification: Scalars['Boolean']['output'];
|
||||
|
@ -1202,12 +1218,12 @@ export type Mutation = {
|
|||
serverInviteCreate: Scalars['Boolean']['output'];
|
||||
/**
|
||||
* Request access to a specific stream
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.create instead.
|
||||
*/
|
||||
streamAccessRequestCreate: StreamAccessRequest;
|
||||
/**
|
||||
* Accept or decline a stream access request. Must be a stream owner to invoke this.
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.use instead.
|
||||
*/
|
||||
streamAccessRequestUse: Scalars['Boolean']['output'];
|
||||
/**
|
||||
|
@ -1264,7 +1280,7 @@ export type Mutation = {
|
|||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectMutations.updateRole instead.
|
||||
*/
|
||||
streamUpdatePermission?: Maybe<Scalars['Boolean']['output']>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use ProjectMutations.batchDelete instead. */
|
||||
streamsDelete: Scalars['Boolean']['output'];
|
||||
/**
|
||||
* Used for broadcasting real time typing status in comment threads. Does not persist any info.
|
||||
|
@ -1581,6 +1597,7 @@ export type MutationWebhookUpdateArgs = {
|
|||
|
||||
export type Object = {
|
||||
__typename?: 'Object';
|
||||
/** @deprecated Not implemented. */
|
||||
applicationId?: Maybe<Scalars['String']['output']>;
|
||||
/**
|
||||
* Get any objects that this object references. In the case of commits, this will give you a commit's constituent objects.
|
||||
|
@ -1620,7 +1637,7 @@ export type ObjectChildrenArgs = {
|
|||
export type ObjectCollection = {
|
||||
__typename?: 'ObjectCollection';
|
||||
cursor?: Maybe<Scalars['String']['output']>;
|
||||
objects: Array<Maybe<Object>>;
|
||||
objects: Array<Object>;
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
|
@ -1681,6 +1698,8 @@ export type Project = {
|
|||
blob?: Maybe<BlobMetadata>;
|
||||
/** Get the metadata collection of blobs stored for this stream. */
|
||||
blobs?: Maybe<BlobMetadataCollection>;
|
||||
/** Get specific project comment/thread by ID */
|
||||
comment?: Maybe<Comment>;
|
||||
/** All comment threads in this project */
|
||||
commentThreads: ProjectCommentCollection;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
|
@ -1690,6 +1709,8 @@ export type Project = {
|
|||
invitedTeam?: Maybe<Array<PendingStreamCollaborator>>;
|
||||
/** Returns a specific model by its ID */
|
||||
model: Model;
|
||||
/** Retrieve a specific project model by its ID */
|
||||
modelByName: Model;
|
||||
/** Return a model tree of children for the specified model name */
|
||||
modelChildrenTree: Array<ModelsTreeItem>;
|
||||
/** Returns a flat list of all models */
|
||||
|
@ -1700,6 +1721,9 @@ export type Project = {
|
|||
*/
|
||||
modelsTree: ModelsTreeItemCollection;
|
||||
name: Scalars['String']['output'];
|
||||
object?: Maybe<Object>;
|
||||
/** Pending project access requests */
|
||||
pendingAccessRequests?: Maybe<Array<ProjectAccessRequest>>;
|
||||
/** Returns a list models that are being created from a file import */
|
||||
pendingImportedModels: Array<FileUpload>;
|
||||
/** Active user's role for this project. `null` if request is not authenticated, or the project is not explicitly shared with you. */
|
||||
|
@ -1743,6 +1767,11 @@ export type ProjectBlobsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectCommentArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectCommentThreadsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']['input']>;
|
||||
filter?: InputMaybe<ProjectCommentsFilter>;
|
||||
|
@ -1755,6 +1784,11 @@ export type ProjectModelArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectModelByNameArgs = {
|
||||
name: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectModelChildrenTreeArgs = {
|
||||
fullName: Scalars['String']['input'];
|
||||
};
|
||||
|
@ -1774,6 +1808,11 @@ export type ProjectModelsTreeArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectObjectArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectPendingImportedModelsArgs = {
|
||||
limit?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
@ -1800,6 +1839,38 @@ export type ProjectWebhooksArgs = {
|
|||
id?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
/** Created when a user requests to become a contributor on a project */
|
||||
export type ProjectAccessRequest = {
|
||||
__typename?: 'ProjectAccessRequest';
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
/** Can only be selected if authed user has proper access */
|
||||
project: Project;
|
||||
projectId: Scalars['String']['output'];
|
||||
requester: LimitedUser;
|
||||
requesterId: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type ProjectAccessRequestMutations = {
|
||||
__typename?: 'ProjectAccessRequestMutations';
|
||||
/** Request access to a specific project */
|
||||
create: ProjectAccessRequest;
|
||||
/** Accept or decline a project access request. Must be a project owner to invoke this. */
|
||||
use: Project;
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccessRequestMutationsCreateArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccessRequestMutationsUseArgs = {
|
||||
accept: Scalars['Boolean']['input'];
|
||||
requestId: Scalars['String']['input'];
|
||||
role?: StreamRole;
|
||||
};
|
||||
|
||||
export type ProjectAutomationCreateInput = {
|
||||
enabled: Scalars['Boolean']['input'];
|
||||
name: Scalars['String']['input'];
|
||||
|
@ -2042,7 +2113,11 @@ export enum ProjectModelsUpdatedMessageType {
|
|||
|
||||
export type ProjectMutations = {
|
||||
__typename?: 'ProjectMutations';
|
||||
/** Access request related mutations */
|
||||
accessRequestMutations: ProjectAccessRequestMutations;
|
||||
automationMutations: ProjectAutomationMutations;
|
||||
/** Batch delete projects */
|
||||
batchDelete: Scalars['Boolean']['output'];
|
||||
/** Create new project */
|
||||
create: Project;
|
||||
/**
|
||||
|
@ -2068,6 +2143,11 @@ export type ProjectMutationsAutomationMutationsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectMutationsBatchDeleteArgs = {
|
||||
ids: Array<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
|
||||
export type ProjectMutationsCreateArgs = {
|
||||
input?: InputMaybe<ProjectCreateInput>;
|
||||
};
|
||||
|
@ -2231,7 +2311,7 @@ export type Query = {
|
|||
automateFunctions: AutomateFunctionCollection;
|
||||
/** Part of the automation/function creation handshake mechanism */
|
||||
automateValidateAuthCode: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.comment instead. */
|
||||
comment?: Maybe<Comment>;
|
||||
/**
|
||||
* This query can be used in the following ways:
|
||||
|
@ -2270,7 +2350,7 @@ export type Query = {
|
|||
stream?: Maybe<Stream>;
|
||||
/**
|
||||
* Get authed user's stream access request
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use User.projectAccessRequest instead.
|
||||
*/
|
||||
streamAccessRequest?: Maybe<StreamAccessRequest>;
|
||||
/**
|
||||
|
@ -2635,7 +2715,7 @@ export type Stream = {
|
|||
* @deprecated Part of the old API surface and will be removed in the future. Use Project.blobs instead.
|
||||
*/
|
||||
blobs?: Maybe<BlobMetadataCollection>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.model instead. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.model or Project.modelByName instead. */
|
||||
branch?: Maybe<Branch>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.models or Project.modelsTree instead. */
|
||||
branches?: Maybe<BranchCollection>;
|
||||
|
@ -2680,11 +2760,11 @@ export type Stream = {
|
|||
/** Whether the stream can be viewed by non-contributors */
|
||||
isPublic: Scalars['Boolean']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.object instead. */
|
||||
object?: Maybe<Object>;
|
||||
/**
|
||||
* Pending stream access requests
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use Project.pendingAccessRequests instead.
|
||||
*/
|
||||
pendingAccessRequests?: Maybe<Array<StreamAccessRequest>>;
|
||||
/** Collaborators who have been invited, but not yet accepted. */
|
||||
|
@ -3188,6 +3268,8 @@ export type User = {
|
|||
name: Scalars['String']['output'];
|
||||
notificationPreferences: Scalars['JSONObject']['output'];
|
||||
profiles?: Maybe<Scalars['JSONObject']['output']>;
|
||||
/** Get pending project access request, that the user made */
|
||||
projectAccessRequest?: Maybe<ProjectAccessRequest>;
|
||||
/** Get all invitations to projects that the active user has */
|
||||
projectInvites: Array<PendingStreamCollaborator>;
|
||||
/** Get projects that the user participates in */
|
||||
|
@ -3253,6 +3335,15 @@ export type UserFavoriteStreamsArgs = {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Full user type, should only be used in the context of admin operations or
|
||||
* when a user is reading/writing info about himself
|
||||
*/
|
||||
export type UserProjectAccessRequestArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Full user type, should only be used in the context of admin operations or
|
||||
* when a user is reading/writing info about himself
|
||||
|
@ -3397,18 +3488,30 @@ export type VersionCreatedTriggerDefinition = {
|
|||
|
||||
export type VersionMutations = {
|
||||
__typename?: 'VersionMutations';
|
||||
create: Version;
|
||||
delete: Scalars['Boolean']['output'];
|
||||
markReceived: Scalars['Boolean']['output'];
|
||||
moveToModel: Model;
|
||||
requestGendoAIRender: Scalars['Boolean']['output'];
|
||||
update: Version;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsCreateArgs = {
|
||||
input: CreateVersionInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsDeleteArgs = {
|
||||
input: DeleteVersionsInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsMarkReceivedArgs = {
|
||||
input: MarkReceivedVersionInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsMoveToModelArgs = {
|
||||
input: MoveVersionsInput;
|
||||
};
|
||||
|
|
|
@ -66,7 +66,7 @@ const getUserAclEntry = async ({ aclTableName, userId, resourceId }) => {
|
|||
* @param {string | null | undefined} userId
|
||||
* @param {string} resourceId
|
||||
* @param {string} requiredRole
|
||||
* @param {import('@/modules/serverinvites/services/operations').TokenResourceIdentifier[] | undefined | null} [userResourceAccessLimits]
|
||||
* @param {import('@/modules/core/domain/tokens/types').TokenResourceIdentifier[] | undefined | null} [userResourceAccessLimits]
|
||||
*/
|
||||
async function authorizeResolver(
|
||||
userId,
|
||||
|
|
|
@ -20,17 +20,49 @@ export type GetWorkspace = (args: GetWorkspaceArgs) => Promise<Workspace | null>
|
|||
|
||||
/** WorkspaceRole */
|
||||
|
||||
export type UpsertWorkspaceRole = (args: WorkspaceAcl) => Promise<void>
|
||||
|
||||
type GetWorkspaceRoleArgs = {
|
||||
type DeleteWorkspaceRoleArgs = {
|
||||
workspaceId: string
|
||||
userId: string
|
||||
}
|
||||
|
||||
export type GetWorkspaceRole = (
|
||||
args: GetWorkspaceRoleArgs
|
||||
export type DeleteWorkspaceRole = (
|
||||
args: DeleteWorkspaceRoleArgs
|
||||
) => Promise<WorkspaceAcl | null>
|
||||
|
||||
type GetWorkspaceRolesArgs = {
|
||||
workspaceId: string
|
||||
}
|
||||
|
||||
/** Get all roles in a given workspaces. */
|
||||
export type GetWorkspaceRoles = (args: GetWorkspaceRolesArgs) => Promise<WorkspaceAcl[]>
|
||||
|
||||
type GetWorkspaceRoleForUserArgs = {
|
||||
userId: string
|
||||
workspaceId: string
|
||||
}
|
||||
|
||||
/** Get role for given user in a specific workspace. */
|
||||
export type GetWorkspaceRoleForUser = (
|
||||
args: GetWorkspaceRoleForUserArgs
|
||||
) => Promise<WorkspaceAcl | null>
|
||||
|
||||
type GetWorkspaceRolesForUserArgs = {
|
||||
userId: string
|
||||
}
|
||||
|
||||
type GetWorkspaceRolesForUserOptions = {
|
||||
/** If provided, limit results to roles in given workspaces. */
|
||||
workspaceIdFilter?: string[]
|
||||
}
|
||||
|
||||
/** Get roles for given user across several (or all) workspaces. */
|
||||
export type GetWorkspaceRolesForUser = (
|
||||
args: GetWorkspaceRolesForUserArgs,
|
||||
options?: GetWorkspaceRolesForUserOptions
|
||||
) => Promise<WorkspaceAcl[]>
|
||||
|
||||
export type UpsertWorkspaceRole = (args: WorkspaceAcl) => Promise<void>
|
||||
|
||||
/** Blob */
|
||||
|
||||
export type StoreBlob = (args: string) => Promise<string>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { BaseError } from '@/modules/shared/errors/base'
|
||||
|
||||
export class WorkspaceAdminRequiredError extends BaseError {
|
||||
static defaultMessage = 'Cannot remove last admin from a workspace'
|
||||
static code = 'WORKSPACE_ADMIN_REQUIRED_ERROR'
|
||||
static statusCode = 400
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
import { Workspace, WorkspaceAcl } from '@/modules/workspaces/domain/types'
|
||||
import {
|
||||
DeleteWorkspaceRole,
|
||||
GetWorkspace,
|
||||
GetWorkspaceRole,
|
||||
GetWorkspaceRoleForUser,
|
||||
GetWorkspaceRoles,
|
||||
GetWorkspaceRolesForUser,
|
||||
UpsertWorkspace,
|
||||
UpsertWorkspaceRole
|
||||
} from '@/modules/workspaces/domain/operations'
|
||||
|
@ -35,21 +38,59 @@ export const upsertWorkspaceFactory =
|
|||
.merge(['description', 'logoUrl', 'name', 'updatedAt'])
|
||||
}
|
||||
|
||||
export const getWorkspaceRoleFactory =
|
||||
({ db }: { db: Knex }): GetWorkspaceRole =>
|
||||
async ({ userId, workspaceId }) => {
|
||||
const acl = await tables
|
||||
.workspacesAcl(db)
|
||||
.select('*')
|
||||
.where({ userId, workspaceId })
|
||||
.first()
|
||||
export const getWorkspaceRolesFactory =
|
||||
({ db }: { db: Knex }): GetWorkspaceRoles =>
|
||||
async ({ workspaceId }) => {
|
||||
return await tables.workspacesAcl(db).select('*').where({ workspaceId })
|
||||
}
|
||||
|
||||
return acl || null
|
||||
export const getWorkspaceRoleForUserFactory =
|
||||
({ db }: { db: Knex }): GetWorkspaceRoleForUser =>
|
||||
async ({ userId, workspaceId }) => {
|
||||
return (
|
||||
(await tables
|
||||
.workspacesAcl(db)
|
||||
.select('*')
|
||||
.where({ userId, workspaceId })
|
||||
.first()) ?? null
|
||||
)
|
||||
}
|
||||
|
||||
export const getWorkspaceRolesForUserFactory =
|
||||
({ db }: { db: Knex }): GetWorkspaceRolesForUser =>
|
||||
async ({ userId }, options) => {
|
||||
const workspaceIdFilter = options?.workspaceIdFilter ?? []
|
||||
|
||||
const query = tables.workspacesAcl(db).select('*').where({ userId })
|
||||
|
||||
if (workspaceIdFilter.length > 0) {
|
||||
query.whereIn('workspaceId', workspaceIdFilter)
|
||||
}
|
||||
|
||||
return await query
|
||||
}
|
||||
|
||||
export const deleteWorkspaceRoleFactory =
|
||||
({ db }: { db: Knex }): DeleteWorkspaceRole =>
|
||||
async ({ userId, workspaceId }) => {
|
||||
const deletedRoles = await tables
|
||||
.workspacesAcl(db)
|
||||
.where({ workspaceId, userId })
|
||||
.delete('*')
|
||||
|
||||
if (deletedRoles.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Given `workspaceId` and `userId` define a primary key for `workspace_acl` table,
|
||||
// query returns either 0 or 1 row in all cases
|
||||
return deletedRoles[0]
|
||||
}
|
||||
|
||||
export const upsertWorkspaceRoleFactory =
|
||||
({ db }: { db: Knex }): UpsertWorkspaceRole =>
|
||||
async ({ userId, workspaceId, role }) => {
|
||||
// Verify requested role is valid workspace role
|
||||
const validRoles = Object.values(Roles.Workspace)
|
||||
if (!validRoles.includes(role)) {
|
||||
throw new Error(`Unexpected workspace role provided: ${role}`)
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import {
|
||||
DeleteWorkspaceRole,
|
||||
EmitWorkspaceEvent,
|
||||
GetWorkspaceRoleForUser,
|
||||
GetWorkspaceRoles,
|
||||
UpsertWorkspaceRole
|
||||
} from '@/modules/workspaces/domain/operations'
|
||||
import { WorkspaceAcl } from '@/modules/workspaces/domain/types'
|
||||
import { WorkspaceAdminRequiredError } from '@/modules/workspaces/errors/workspace'
|
||||
import { isUserLastWorkspaceAdmin } from '@/modules/workspaces/utils/isUserLastWorkspaceAdmin'
|
||||
import { WorkspaceEvents } from '@/modules/workspacesCore/domain/events'
|
||||
|
||||
type WorkspaceRoleDeleteArgs = {
|
||||
userId: string
|
||||
workspaceId: string
|
||||
}
|
||||
|
||||
export const deleteWorkspaceRoleFactory =
|
||||
({
|
||||
getWorkspaceRoles,
|
||||
deleteWorkspaceRole,
|
||||
emitWorkspaceEvent
|
||||
}: {
|
||||
getWorkspaceRoles: GetWorkspaceRoles
|
||||
deleteWorkspaceRole: DeleteWorkspaceRole
|
||||
emitWorkspaceEvent: EmitWorkspaceEvent
|
||||
}) =>
|
||||
async ({
|
||||
userId,
|
||||
workspaceId
|
||||
}: WorkspaceRoleDeleteArgs): Promise<WorkspaceAcl | null> => {
|
||||
const workspaceRoles = await getWorkspaceRoles({ workspaceId })
|
||||
|
||||
if (isUserLastWorkspaceAdmin(workspaceRoles, userId)) {
|
||||
throw new WorkspaceAdminRequiredError()
|
||||
}
|
||||
|
||||
const deletedRole = await deleteWorkspaceRole({ userId, workspaceId })
|
||||
|
||||
if (!deletedRole) {
|
||||
return null
|
||||
}
|
||||
|
||||
emitWorkspaceEvent({ event: WorkspaceEvents.RoleDeleted, payload: deletedRole })
|
||||
|
||||
return deletedRole
|
||||
}
|
||||
|
||||
type WorkspaceRoleGetArgs = {
|
||||
userId: string
|
||||
workspaceId: string
|
||||
}
|
||||
|
||||
export const getWorkspaceRoleFactory =
|
||||
({ getWorkspaceRoleForUser }: { getWorkspaceRoleForUser: GetWorkspaceRoleForUser }) =>
|
||||
async ({
|
||||
userId,
|
||||
workspaceId
|
||||
}: WorkspaceRoleGetArgs): Promise<WorkspaceAcl | null> => {
|
||||
return await getWorkspaceRoleForUser({ userId, workspaceId })
|
||||
}
|
||||
|
||||
export const setWorkspaceRoleFactory =
|
||||
({
|
||||
getWorkspaceRoles,
|
||||
upsertWorkspaceRole,
|
||||
emitWorkspaceEvent
|
||||
}: {
|
||||
getWorkspaceRoles: GetWorkspaceRoles
|
||||
upsertWorkspaceRole: UpsertWorkspaceRole
|
||||
emitWorkspaceEvent: EmitWorkspaceEvent
|
||||
}) =>
|
||||
async ({ userId, workspaceId, role }: WorkspaceAcl): Promise<void> => {
|
||||
const workspaceRoles = await getWorkspaceRoles({ workspaceId })
|
||||
|
||||
if (
|
||||
isUserLastWorkspaceAdmin(workspaceRoles, userId) &&
|
||||
role !== 'workspace:admin'
|
||||
) {
|
||||
throw new WorkspaceAdminRequiredError()
|
||||
}
|
||||
|
||||
await upsertWorkspaceRole({ userId, workspaceId, role })
|
||||
|
||||
await emitWorkspaceEvent({
|
||||
event: WorkspaceEvents.RoleUpdated,
|
||||
payload: { userId, workspaceId, role }
|
||||
})
|
||||
}
|
|
@ -1,18 +1,43 @@
|
|||
import {
|
||||
deleteWorkspaceRoleFactory,
|
||||
getWorkspaceRoleForUserFactory,
|
||||
getWorkspaceFactory,
|
||||
upsertWorkspaceFactory,
|
||||
upsertWorkspaceRoleFactory
|
||||
upsertWorkspaceRoleFactory,
|
||||
getWorkspaceRolesFactory,
|
||||
getWorkspaceRolesForUserFactory
|
||||
} from '@/modules/workspaces/repositories/workspaces'
|
||||
import db from '@/db/knex'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import { expect } from 'chai'
|
||||
import { Workspace, WorkspaceAcl } from '@/modules/workspaces/domain/types'
|
||||
import { expectToThrow } from '@/test/assertionHelper'
|
||||
import { BasicTestUser, createTestUser } from '@/test/authHelper'
|
||||
|
||||
const getWorkspace = getWorkspaceFactory({ db })
|
||||
const upsertWorkspace = upsertWorkspaceFactory({ db })
|
||||
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({ db })
|
||||
const getWorkspaceRoles = getWorkspaceRolesFactory({ db })
|
||||
const getWorkspaceRoleForUser = getWorkspaceRoleForUserFactory({ db })
|
||||
const getWorkspaceRolesForUser = getWorkspaceRolesForUserFactory({ db })
|
||||
const upsertWorkspaceRole = upsertWorkspaceRoleFactory({ db })
|
||||
|
||||
const createAndStoreTestUser = async (): Promise<BasicTestUser> => {
|
||||
const testId = cryptoRandomString({ length: 6 })
|
||||
|
||||
const userRecord: BasicTestUser = {
|
||||
name: `test-user-${testId}`,
|
||||
email: `test-user-${testId}@example.org`,
|
||||
password: '',
|
||||
id: '',
|
||||
role: 'server:user'
|
||||
}
|
||||
|
||||
await createTestUser(userRecord)
|
||||
|
||||
return userRecord
|
||||
}
|
||||
|
||||
const createAndStoreTestWorkspace = async (): Promise<Workspace> => {
|
||||
const workspace: Workspace = {
|
||||
id: cryptoRandomString({ length: 10 }),
|
||||
|
@ -79,6 +104,140 @@ describe('Workspace repositories', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('deleteWorkspaceRoleFactory creates a function, that', () => {
|
||||
it('deletes specified workspace role', async () => {
|
||||
const { id: userId } = await createAndStoreTestUser()
|
||||
const { id: workspaceId } = await createAndStoreTestWorkspace()
|
||||
|
||||
await upsertWorkspaceRole({ userId, workspaceId, role: 'workspace:member' })
|
||||
await deleteWorkspaceRole({ userId, workspaceId })
|
||||
|
||||
const role = await getWorkspaceRoleForUser({ userId, workspaceId })
|
||||
|
||||
expect(role).to.be.null
|
||||
})
|
||||
it('returns deleted workspace role', async () => {
|
||||
const { id: userId } = await createAndStoreTestUser()
|
||||
const { id: workspaceId } = await createAndStoreTestWorkspace()
|
||||
|
||||
const createdRole: WorkspaceAcl = {
|
||||
userId,
|
||||
workspaceId,
|
||||
role: 'workspace:member'
|
||||
}
|
||||
await upsertWorkspaceRole(createdRole)
|
||||
const deletedRole = await deleteWorkspaceRole({ userId, workspaceId })
|
||||
|
||||
expect(deletedRole).to.deep.equal(createdRole)
|
||||
})
|
||||
it('return null if role does not exist', async () => {
|
||||
const deletedRole = await deleteWorkspaceRole({ userId: '', workspaceId: '' })
|
||||
|
||||
expect(deletedRole).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
describe('getWorkspaceRolesFactory creates a function, that', () => {
|
||||
it('returns all roles in a given workspace', async () => {
|
||||
const { id: workspaceId } = await createAndStoreTestWorkspace()
|
||||
|
||||
const { id: userIdA } = await createAndStoreTestUser()
|
||||
const { id: userIdB } = await createAndStoreTestUser()
|
||||
|
||||
await upsertWorkspaceRole({
|
||||
workspaceId,
|
||||
userId: userIdA,
|
||||
role: 'workspace:admin'
|
||||
})
|
||||
await upsertWorkspaceRole({
|
||||
workspaceId,
|
||||
userId: userIdB,
|
||||
role: 'workspace:admin'
|
||||
})
|
||||
|
||||
const workspaceRoles = await getWorkspaceRoles({ workspaceId })
|
||||
|
||||
expect(workspaceRoles.length).to.equal(2)
|
||||
expect(workspaceRoles.some(({ userId }) => userId === userIdA)).to.be.true
|
||||
expect(workspaceRoles.some(({ userId }) => userId === userIdB)).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
describe('getWorkspaceRoleForUserFactory creates a function, that', () => {
|
||||
it('returns the current role for a given user in a given workspace', async () => {
|
||||
const { id: userId } = await createAndStoreTestUser()
|
||||
const { id: workspaceId } = await createAndStoreTestWorkspace()
|
||||
|
||||
await upsertWorkspaceRole({ workspaceId, userId, role: 'workspace:admin' })
|
||||
|
||||
const workspaceRole = await getWorkspaceRoleForUser({ userId, workspaceId })
|
||||
|
||||
expect(workspaceRole).to.not.be.null
|
||||
expect(workspaceRole?.userId).to.equal(userId)
|
||||
})
|
||||
it('returns `null` if the given user does not have a role in the given workspace', async () => {
|
||||
const workspaceRole = await getWorkspaceRoleForUser({
|
||||
userId: 'invalid-user-id',
|
||||
workspaceId: 'invalid-workspace-id'
|
||||
})
|
||||
|
||||
expect(workspaceRole).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
describe('getWorkspaceRolesForUserFactory creates a function, that', () => {
|
||||
it('returns the current role for a given user across all workspaces', async () => {
|
||||
const { id: userId } = await createAndStoreTestUser()
|
||||
|
||||
const { id: workspaceIdA } = await createAndStoreTestWorkspace()
|
||||
const { id: workspaceIdB } = await createAndStoreTestWorkspace()
|
||||
|
||||
await upsertWorkspaceRole({
|
||||
workspaceId: workspaceIdA,
|
||||
userId,
|
||||
role: 'workspace:admin'
|
||||
})
|
||||
await upsertWorkspaceRole({
|
||||
workspaceId: workspaceIdB,
|
||||
userId,
|
||||
role: 'workspace:admin'
|
||||
})
|
||||
|
||||
const workspaceRoles = await getWorkspaceRolesForUser({ userId })
|
||||
|
||||
expect(workspaceRoles.length).to.equal(2)
|
||||
expect(workspaceRoles.some(({ workspaceId }) => workspaceId === workspaceIdA)).to
|
||||
.be.true
|
||||
expect(workspaceRoles.some(({ workspaceId }) => workspaceId === workspaceIdB)).to
|
||||
.be.true
|
||||
})
|
||||
it('returns the current role for workspaces specified by the workspace id filter, if provided', async () => {
|
||||
const { id: userId } = await createAndStoreTestUser()
|
||||
|
||||
const { id: workspaceIdA } = await createAndStoreTestWorkspace()
|
||||
const { id: workspaceIdB } = await createAndStoreTestWorkspace()
|
||||
|
||||
await upsertWorkspaceRole({
|
||||
workspaceId: workspaceIdA,
|
||||
userId,
|
||||
role: 'workspace:admin'
|
||||
})
|
||||
await upsertWorkspaceRole({
|
||||
workspaceId: workspaceIdB,
|
||||
userId,
|
||||
role: 'workspace:admin'
|
||||
})
|
||||
|
||||
const workspaceRoles = await getWorkspaceRolesForUser(
|
||||
{ userId },
|
||||
{ workspaceIdFilter: [workspaceIdA] }
|
||||
)
|
||||
|
||||
expect(workspaceRoles.length).to.equal(1)
|
||||
expect(workspaceRoles[0].workspaceId).to.equal(workspaceIdA)
|
||||
})
|
||||
})
|
||||
|
||||
describe('upsertWorkspaceRoleFactory creates a function, that', () => {
|
||||
it('throws if an unknown role is provided', async () => {
|
||||
const role: WorkspaceAcl = {
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
import { WorkspaceAcl } from '@/modules/workspaces/domain/types'
|
||||
import {
|
||||
deleteWorkspaceRoleFactory,
|
||||
setWorkspaceRoleFactory
|
||||
} from '@/modules/workspaces/services/workspaceRoleCreation'
|
||||
import { WorkspaceEvents } from '@/modules/workspacesCore/domain/events'
|
||||
import { expectToThrow } from '@/test/assertionHelper'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import { expect } from 'chai'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
|
||||
describe('Workspace role services', () => {
|
||||
describe('deleteWorkspaceRoleFactory creates a function, that', () => {
|
||||
it('deletes the workspace role', async () => {
|
||||
const userId = cryptoRandomString({ length: 10 })
|
||||
const workspaceId = cryptoRandomString({ length: 10 })
|
||||
|
||||
const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Member }
|
||||
|
||||
let storedRoles: WorkspaceAcl[] = [role]
|
||||
|
||||
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
|
||||
getWorkspaceRoles: async () => storedRoles,
|
||||
deleteWorkspaceRole: async ({ userId, workspaceId }) => {
|
||||
const role = storedRoles.find(
|
||||
(r) => r.userId === userId && r.workspaceId === workspaceId
|
||||
)
|
||||
|
||||
storedRoles = storedRoles.filter((r) => r.userId !== userId)
|
||||
|
||||
return role ?? null
|
||||
},
|
||||
emitWorkspaceEvent: async () => []
|
||||
})
|
||||
|
||||
const deletedRole = await deleteWorkspaceRole({ userId, workspaceId })
|
||||
|
||||
expect(storedRoles.length).to.equal(0)
|
||||
expect(deletedRole).to.deep.equal(role)
|
||||
})
|
||||
it('emits a role-deleted event', async () => {
|
||||
const eventData = {
|
||||
isCalled: false,
|
||||
event: '',
|
||||
payload: {}
|
||||
}
|
||||
|
||||
const userId = cryptoRandomString({ length: 10 })
|
||||
const workspaceId = cryptoRandomString({ length: 10 })
|
||||
|
||||
const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Member }
|
||||
|
||||
const storedRoles: WorkspaceAcl[] = [role]
|
||||
|
||||
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
|
||||
getWorkspaceRoles: async () => storedRoles,
|
||||
deleteWorkspaceRole: async () => {
|
||||
return storedRoles[0]
|
||||
},
|
||||
emitWorkspaceEvent: async ({ event, payload }) => {
|
||||
eventData.isCalled = true
|
||||
eventData.event = event
|
||||
eventData.payload = payload
|
||||
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
await deleteWorkspaceRole({ userId, workspaceId })
|
||||
|
||||
expect(eventData.isCalled).to.be.true
|
||||
expect(eventData.event).to.equal(WorkspaceEvents.RoleDeleted)
|
||||
expect(eventData.payload).to.deep.equal(role)
|
||||
})
|
||||
it('throws if attempting to delete the last admin from a workspace', async () => {
|
||||
const userId = cryptoRandomString({ length: 10 })
|
||||
const workspaceId = cryptoRandomString({ length: 10 })
|
||||
|
||||
const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Admin }
|
||||
|
||||
let storedRoles: WorkspaceAcl[] = [role]
|
||||
|
||||
const deleteWorkspaceRole = deleteWorkspaceRoleFactory({
|
||||
getWorkspaceRoles: async () => storedRoles,
|
||||
deleteWorkspaceRole: async ({ userId, workspaceId }) => {
|
||||
const role = storedRoles.find(
|
||||
(r) => r.userId === userId && r.workspaceId === workspaceId
|
||||
)
|
||||
|
||||
storedRoles = storedRoles.filter((r) => r.userId !== userId)
|
||||
|
||||
return role ?? null
|
||||
},
|
||||
emitWorkspaceEvent: async () => []
|
||||
})
|
||||
|
||||
await expectToThrow(() => deleteWorkspaceRole({ userId, workspaceId }))
|
||||
})
|
||||
})
|
||||
|
||||
describe('setWorkspaceRoleFactory creates a function, that', () => {
|
||||
it('sets the workspace role', async () => {
|
||||
const userId = cryptoRandomString({ length: 10 })
|
||||
const workspaceId = cryptoRandomString({ length: 10 })
|
||||
|
||||
const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Member }
|
||||
|
||||
const storedRoles: WorkspaceAcl[] = []
|
||||
|
||||
const setWorkspaceRole = setWorkspaceRoleFactory({
|
||||
getWorkspaceRoles: async () => storedRoles,
|
||||
upsertWorkspaceRole: async (role) => {
|
||||
storedRoles.push(role)
|
||||
},
|
||||
emitWorkspaceEvent: async () => []
|
||||
})
|
||||
|
||||
await setWorkspaceRole(role)
|
||||
|
||||
expect(storedRoles.length).to.equal(1)
|
||||
expect(storedRoles[0]).to.deep.equal(role)
|
||||
})
|
||||
it('emits a role-updated event', async () => {
|
||||
const eventData = {
|
||||
isCalled: false,
|
||||
event: '',
|
||||
payload: {}
|
||||
}
|
||||
|
||||
const userId = cryptoRandomString({ length: 10 })
|
||||
const workspaceId = cryptoRandomString({ length: 10 })
|
||||
|
||||
const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Member }
|
||||
|
||||
const setWorkspaceRole = setWorkspaceRoleFactory({
|
||||
getWorkspaceRoles: async () => [],
|
||||
upsertWorkspaceRole: async () => {},
|
||||
emitWorkspaceEvent: async ({ event, payload }) => {
|
||||
eventData.isCalled = true
|
||||
eventData.event = event
|
||||
eventData.payload = payload
|
||||
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
await setWorkspaceRole(role)
|
||||
|
||||
expect(eventData.isCalled).to.be.true
|
||||
expect(eventData.event).to.equal(WorkspaceEvents.RoleUpdated)
|
||||
expect(eventData.payload).to.deep.equal(role)
|
||||
})
|
||||
it('throws if attempting to remove the last admin in a workspace', async () => {
|
||||
const userId = cryptoRandomString({ length: 10 })
|
||||
const workspaceId = cryptoRandomString({ length: 10 })
|
||||
|
||||
const role: WorkspaceAcl = { userId, workspaceId, role: Roles.Workspace.Admin }
|
||||
|
||||
const storedRoles: WorkspaceAcl[] = [role]
|
||||
|
||||
const setWorkspaceRole = setWorkspaceRoleFactory({
|
||||
getWorkspaceRoles: async () => storedRoles,
|
||||
upsertWorkspaceRole: async () => {},
|
||||
emitWorkspaceEvent: async () => []
|
||||
})
|
||||
|
||||
await expectToThrow(() =>
|
||||
setWorkspaceRole({ ...role, role: Roles.Workspace.Member })
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,56 @@
|
|||
import { isUserLastWorkspaceAdmin } from '@/modules/workspaces/utils/isUserLastWorkspaceAdmin'
|
||||
import { WorkspaceAcl } from '@/modules/workspaces/domain/types'
|
||||
import { expect } from 'chai'
|
||||
import { Roles } from '@speckle/shared'
|
||||
|
||||
describe('given a workspace with several admins', () => {
|
||||
const workspaceRoles: WorkspaceAcl[] = [
|
||||
{ workspaceId: 'workspace-id', userId: 'non-admin', role: Roles.Workspace.Member },
|
||||
{ workspaceId: 'workspace-id', userId: 'admin-a', role: Roles.Workspace.Admin },
|
||||
{ workspaceId: 'workspace-id', userId: 'admin-b', role: Roles.Workspace.Admin }
|
||||
]
|
||||
|
||||
describe('when testing a non-admin user', () => {
|
||||
it('should return false', () => {
|
||||
expect(isUserLastWorkspaceAdmin(workspaceRoles, 'non-admin')).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
describe('when testing an admin user', () => {
|
||||
it('should return false', () => {
|
||||
expect(isUserLastWorkspaceAdmin(workspaceRoles, 'admin-a')).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a workspace with one admin', () => {
|
||||
const workspaceRoles: WorkspaceAcl[] = [
|
||||
{ workspaceId: 'workspace-id', userId: 'non-admin', role: Roles.Workspace.Member },
|
||||
{ workspaceId: 'workspace-id', userId: 'admin', role: Roles.Workspace.Admin }
|
||||
]
|
||||
|
||||
describe('when testing a non-admin user', () => {
|
||||
it('should return false', () => {
|
||||
expect(isUserLastWorkspaceAdmin(workspaceRoles, 'non-admin')).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
describe('when testing an admin user', () => {
|
||||
it('should return true', () => {
|
||||
expect(isUserLastWorkspaceAdmin(workspaceRoles, 'admin')).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a workspace', () => {
|
||||
const workspaceRoles: WorkspaceAcl[] = [
|
||||
{ workspaceId: 'workspace-id', userId: 'non-admin', role: Roles.Workspace.Member },
|
||||
{ workspaceId: 'workspace-id', userId: 'admin', role: Roles.Workspace.Admin }
|
||||
]
|
||||
|
||||
describe('when testing a non-workspace user', () => {
|
||||
it('should return false', () => {
|
||||
expect(isUserLastWorkspaceAdmin(workspaceRoles, 'random-id')).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,13 @@
|
|||
import { WorkspaceAcl } from '@/modules/workspaces/domain/types'
|
||||
|
||||
export const isUserLastWorkspaceAdmin = (
|
||||
workspaceRoles: WorkspaceAcl[],
|
||||
userId: string
|
||||
): boolean => {
|
||||
const workspaceAdmins = workspaceRoles.filter(
|
||||
({ role }) => role === 'workspace:admin'
|
||||
)
|
||||
const isUserAdmin = workspaceAdmins.some((role) => role.userId === userId)
|
||||
|
||||
return isUserAdmin && workspaceAdmins.length === 1
|
||||
}
|
|
@ -1,13 +1,19 @@
|
|||
import { Workspace } from '@/modules/workspaces/domain/types'
|
||||
import { Workspace, WorkspaceAcl } from '@/modules/workspaces/domain/types'
|
||||
|
||||
export const WorkspaceEvents = {
|
||||
Created: 'created'
|
||||
Created: 'created',
|
||||
RoleDeleted: 'role-deleted',
|
||||
RoleUpdated: 'role-updated'
|
||||
} as const
|
||||
|
||||
export type WorkspaceEvents = (typeof WorkspaceEvents)[keyof typeof WorkspaceEvents]
|
||||
|
||||
type WorkspaceCreatedPayload = Workspace
|
||||
type WorkspaceRoleDeletedPayload = WorkspaceAcl
|
||||
type WorkspaceRoleUpdatedPayload = WorkspaceAcl
|
||||
|
||||
export type WorkspaceEventsPayloads = {
|
||||
[WorkspaceEvents.Created]: WorkspaceCreatedPayload
|
||||
[WorkspaceEvents.RoleDeleted]: WorkspaceRoleDeletedPayload
|
||||
[WorkspaceEvents.RoleUpdated]: WorkspaceRoleUpdatedPayload
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ export type Scalars = {
|
|||
BigInt: { input: bigint; output: bigint; }
|
||||
/** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */
|
||||
DateTime: { input: string; output: string; }
|
||||
EmailAddress: { input: any; output: any; }
|
||||
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
||||
JSONObject: { input: Record<string, unknown>; output: Record<string, unknown>; }
|
||||
};
|
||||
|
@ -802,6 +801,16 @@ export type CreateModelInput = {
|
|||
projectId: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type CreateVersionInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
modelId: Scalars['String']['input'];
|
||||
objectId: Scalars['String']['input'];
|
||||
parents?: InputMaybe<Array<Scalars['String']['input']>>;
|
||||
projectId: Scalars['String']['input'];
|
||||
sourceApplication?: InputMaybe<Scalars['String']['input']>;
|
||||
totalChildrenCount?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
||||
export type DeleteModelInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
projectId: Scalars['ID']['input'];
|
||||
|
@ -992,6 +1001,13 @@ export type LimitedUserTimelineArgs = {
|
|||
limit?: Scalars['Int']['input'];
|
||||
};
|
||||
|
||||
export type MarkReceivedVersionInput = {
|
||||
message?: InputMaybe<Scalars['String']['input']>;
|
||||
projectId: Scalars['String']['input'];
|
||||
sourceApplication: Scalars['String']['input'];
|
||||
versionId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type Model = {
|
||||
__typename?: 'Model';
|
||||
author: LimitedUser;
|
||||
|
@ -1161,11 +1177,11 @@ export type Mutation = {
|
|||
* @deprecated Use commentMutations version
|
||||
*/
|
||||
commentView: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.create instead. */
|
||||
commitCreate: Scalars['String']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.delete instead. */
|
||||
commitDelete: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.markReceived instead. */
|
||||
commitReceive: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use VersionMutations.update/moveToModel instead. */
|
||||
commitUpdate: Scalars['Boolean']['output'];
|
||||
|
@ -1191,7 +1207,7 @@ export type Mutation = {
|
|||
inviteResend: Scalars['Boolean']['output'];
|
||||
modelMutations: ModelMutations;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
objectCreate: Array<Maybe<Scalars['String']['output']>>;
|
||||
objectCreate: Array<Scalars['String']['output']>;
|
||||
projectMutations: ProjectMutations;
|
||||
/** (Re-)send the account verification e-mail */
|
||||
requestVerification: Scalars['Boolean']['output'];
|
||||
|
@ -1203,12 +1219,12 @@ export type Mutation = {
|
|||
serverInviteCreate: Scalars['Boolean']['output'];
|
||||
/**
|
||||
* Request access to a specific stream
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.create instead.
|
||||
*/
|
||||
streamAccessRequestCreate: StreamAccessRequest;
|
||||
/**
|
||||
* Accept or decline a stream access request. Must be a stream owner to invoke this.
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectAccessRequestMutations.use instead.
|
||||
*/
|
||||
streamAccessRequestUse: Scalars['Boolean']['output'];
|
||||
/**
|
||||
|
@ -1265,7 +1281,7 @@ export type Mutation = {
|
|||
* @deprecated Part of the old API surface and will be removed in the future. Use ProjectMutations.updateRole instead.
|
||||
*/
|
||||
streamUpdatePermission?: Maybe<Scalars['Boolean']['output']>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use ProjectMutations.batchDelete instead. */
|
||||
streamsDelete: Scalars['Boolean']['output'];
|
||||
/**
|
||||
* Used for broadcasting real time typing status in comment threads. Does not persist any info.
|
||||
|
@ -1582,6 +1598,7 @@ export type MutationWebhookUpdateArgs = {
|
|||
|
||||
export type Object = {
|
||||
__typename?: 'Object';
|
||||
/** @deprecated Not implemented. */
|
||||
applicationId?: Maybe<Scalars['String']['output']>;
|
||||
/**
|
||||
* Get any objects that this object references. In the case of commits, this will give you a commit's constituent objects.
|
||||
|
@ -1621,7 +1638,7 @@ export type ObjectChildrenArgs = {
|
|||
export type ObjectCollection = {
|
||||
__typename?: 'ObjectCollection';
|
||||
cursor?: Maybe<Scalars['String']['output']>;
|
||||
objects: Array<Maybe<Object>>;
|
||||
objects: Array<Object>;
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
|
@ -1682,6 +1699,8 @@ export type Project = {
|
|||
blob?: Maybe<BlobMetadata>;
|
||||
/** Get the metadata collection of blobs stored for this stream. */
|
||||
blobs?: Maybe<BlobMetadataCollection>;
|
||||
/** Get specific project comment/thread by ID */
|
||||
comment?: Maybe<Comment>;
|
||||
/** All comment threads in this project */
|
||||
commentThreads: ProjectCommentCollection;
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
|
@ -1691,6 +1710,8 @@ export type Project = {
|
|||
invitedTeam?: Maybe<Array<PendingStreamCollaborator>>;
|
||||
/** Returns a specific model by its ID */
|
||||
model: Model;
|
||||
/** Retrieve a specific project model by its ID */
|
||||
modelByName: Model;
|
||||
/** Return a model tree of children for the specified model name */
|
||||
modelChildrenTree: Array<ModelsTreeItem>;
|
||||
/** Returns a flat list of all models */
|
||||
|
@ -1701,6 +1722,9 @@ export type Project = {
|
|||
*/
|
||||
modelsTree: ModelsTreeItemCollection;
|
||||
name: Scalars['String']['output'];
|
||||
object?: Maybe<Object>;
|
||||
/** Pending project access requests */
|
||||
pendingAccessRequests?: Maybe<Array<ProjectAccessRequest>>;
|
||||
/** Returns a list models that are being created from a file import */
|
||||
pendingImportedModels: Array<FileUpload>;
|
||||
/** Active user's role for this project. `null` if request is not authenticated, or the project is not explicitly shared with you. */
|
||||
|
@ -1744,6 +1768,11 @@ export type ProjectBlobsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectCommentArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectCommentThreadsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']['input']>;
|
||||
filter?: InputMaybe<ProjectCommentsFilter>;
|
||||
|
@ -1756,6 +1785,11 @@ export type ProjectModelArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectModelByNameArgs = {
|
||||
name: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectModelChildrenTreeArgs = {
|
||||
fullName: Scalars['String']['input'];
|
||||
};
|
||||
|
@ -1775,6 +1809,11 @@ export type ProjectModelsTreeArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectObjectArgs = {
|
||||
id: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectPendingImportedModelsArgs = {
|
||||
limit?: InputMaybe<Scalars['Int']['input']>;
|
||||
};
|
||||
|
@ -1801,6 +1840,38 @@ export type ProjectWebhooksArgs = {
|
|||
id?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
/** Created when a user requests to become a contributor on a project */
|
||||
export type ProjectAccessRequest = {
|
||||
__typename?: 'ProjectAccessRequest';
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
id: Scalars['ID']['output'];
|
||||
/** Can only be selected if authed user has proper access */
|
||||
project: Project;
|
||||
projectId: Scalars['String']['output'];
|
||||
requester: LimitedUser;
|
||||
requesterId: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type ProjectAccessRequestMutations = {
|
||||
__typename?: 'ProjectAccessRequestMutations';
|
||||
/** Request access to a specific project */
|
||||
create: ProjectAccessRequest;
|
||||
/** Accept or decline a project access request. Must be a project owner to invoke this. */
|
||||
use: Project;
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccessRequestMutationsCreateArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type ProjectAccessRequestMutationsUseArgs = {
|
||||
accept: Scalars['Boolean']['input'];
|
||||
requestId: Scalars['String']['input'];
|
||||
role?: StreamRole;
|
||||
};
|
||||
|
||||
export type ProjectAutomationCreateInput = {
|
||||
enabled: Scalars['Boolean']['input'];
|
||||
name: Scalars['String']['input'];
|
||||
|
@ -2043,7 +2114,11 @@ export enum ProjectModelsUpdatedMessageType {
|
|||
|
||||
export type ProjectMutations = {
|
||||
__typename?: 'ProjectMutations';
|
||||
/** Access request related mutations */
|
||||
accessRequestMutations: ProjectAccessRequestMutations;
|
||||
automationMutations: ProjectAutomationMutations;
|
||||
/** Batch delete projects */
|
||||
batchDelete: Scalars['Boolean']['output'];
|
||||
/** Create new project */
|
||||
create: Project;
|
||||
/**
|
||||
|
@ -2069,6 +2144,11 @@ export type ProjectMutationsAutomationMutationsArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type ProjectMutationsBatchDeleteArgs = {
|
||||
ids: Array<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
|
||||
export type ProjectMutationsCreateArgs = {
|
||||
input?: InputMaybe<ProjectCreateInput>;
|
||||
};
|
||||
|
@ -2232,7 +2312,7 @@ export type Query = {
|
|||
automateFunctions: AutomateFunctionCollection;
|
||||
/** Part of the automation/function creation handshake mechanism */
|
||||
automateValidateAuthCode: Scalars['Boolean']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.comment instead. */
|
||||
comment?: Maybe<Comment>;
|
||||
/**
|
||||
* This query can be used in the following ways:
|
||||
|
@ -2271,7 +2351,7 @@ export type Query = {
|
|||
stream?: Maybe<Stream>;
|
||||
/**
|
||||
* Get authed user's stream access request
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use User.projectAccessRequest instead.
|
||||
*/
|
||||
streamAccessRequest?: Maybe<StreamAccessRequest>;
|
||||
/**
|
||||
|
@ -2636,7 +2716,7 @@ export type Stream = {
|
|||
* @deprecated Part of the old API surface and will be removed in the future. Use Project.blobs instead.
|
||||
*/
|
||||
blobs?: Maybe<BlobMetadataCollection>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.model instead. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.model or Project.modelByName instead. */
|
||||
branch?: Maybe<Branch>;
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.models or Project.modelsTree instead. */
|
||||
branches?: Maybe<BranchCollection>;
|
||||
|
@ -2681,11 +2761,11 @@ export type Stream = {
|
|||
/** Whether the stream can be viewed by non-contributors */
|
||||
isPublic: Scalars['Boolean']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. */
|
||||
/** @deprecated Part of the old API surface and will be removed in the future. Use Project.object instead. */
|
||||
object?: Maybe<Object>;
|
||||
/**
|
||||
* Pending stream access requests
|
||||
* @deprecated Part of the old API surface and will be removed in the future.
|
||||
* @deprecated Part of the old API surface and will be removed in the future. Use Project.pendingAccessRequests instead.
|
||||
*/
|
||||
pendingAccessRequests?: Maybe<Array<StreamAccessRequest>>;
|
||||
/** Collaborators who have been invited, but not yet accepted. */
|
||||
|
@ -3189,6 +3269,8 @@ export type User = {
|
|||
name: Scalars['String']['output'];
|
||||
notificationPreferences: Scalars['JSONObject']['output'];
|
||||
profiles?: Maybe<Scalars['JSONObject']['output']>;
|
||||
/** Get pending project access request, that the user made */
|
||||
projectAccessRequest?: Maybe<ProjectAccessRequest>;
|
||||
/** Get all invitations to projects that the active user has */
|
||||
projectInvites: Array<PendingStreamCollaborator>;
|
||||
/** Get projects that the user participates in */
|
||||
|
@ -3254,6 +3336,15 @@ export type UserFavoriteStreamsArgs = {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Full user type, should only be used in the context of admin operations or
|
||||
* when a user is reading/writing info about himself
|
||||
*/
|
||||
export type UserProjectAccessRequestArgs = {
|
||||
projectId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Full user type, should only be used in the context of admin operations or
|
||||
* when a user is reading/writing info about himself
|
||||
|
@ -3398,18 +3489,30 @@ export type VersionCreatedTriggerDefinition = {
|
|||
|
||||
export type VersionMutations = {
|
||||
__typename?: 'VersionMutations';
|
||||
create: Version;
|
||||
delete: Scalars['Boolean']['output'];
|
||||
markReceived: Scalars['Boolean']['output'];
|
||||
moveToModel: Model;
|
||||
requestGendoAIRender: Scalars['Boolean']['output'];
|
||||
update: Version;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsCreateArgs = {
|
||||
input: CreateVersionInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsDeleteArgs = {
|
||||
input: DeleteVersionsInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsMarkReceivedArgs = {
|
||||
input: MarkReceivedVersionInput;
|
||||
};
|
||||
|
||||
|
||||
export type VersionMutationsMoveToModelArgs = {
|
||||
input: MoveVersionsInput;
|
||||
};
|
||||
|
@ -3714,6 +3817,67 @@ export type DeleteCommitsMutationVariables = Exact<{
|
|||
|
||||
export type DeleteCommitsMutation = { __typename?: 'Mutation', commitsDelete: boolean };
|
||||
|
||||
export type CreateProjectModelMutationVariables = Exact<{
|
||||
input: CreateModelInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateProjectModelMutation = { __typename?: 'Mutation', modelMutations: { __typename?: 'ModelMutations', create: { __typename?: 'Model', id: string, name: string, description?: string | null } } };
|
||||
|
||||
export type FindProjectModelByNameQueryVariables = Exact<{
|
||||
projectId: Scalars['String']['input'];
|
||||
name: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type FindProjectModelByNameQuery = { __typename?: 'Query', project: { __typename?: 'Project', modelByName: { __typename?: 'Model', id: string, name: string, description?: string | null } } };
|
||||
|
||||
export type BasicProjectAccessRequestFieldsFragment = { __typename?: 'ProjectAccessRequest', id: string, requesterId: string, projectId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name: string } };
|
||||
|
||||
export type CreateProjectAccessRequestMutationVariables = Exact<{
|
||||
projectId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateProjectAccessRequestMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', accessRequestMutations: { __typename?: 'ProjectAccessRequestMutations', create: { __typename?: 'ProjectAccessRequest', id: string, requesterId: string, projectId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name: string } } } } };
|
||||
|
||||
export type GetActiveUserProjectAccessRequestQueryVariables = Exact<{
|
||||
projectId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetActiveUserProjectAccessRequestQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', projectAccessRequest?: { __typename?: 'ProjectAccessRequest', id: string, requesterId: string, projectId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name: string } } | null } | null };
|
||||
|
||||
export type GetActiveUserFullProjectAccessRequestQueryVariables = Exact<{
|
||||
projectId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetActiveUserFullProjectAccessRequestQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', projectAccessRequest?: { __typename?: 'ProjectAccessRequest', id: string, requesterId: string, projectId: string, createdAt: string, project: { __typename?: 'Project', id: string, name: string }, requester: { __typename?: 'LimitedUser', id: string, name: string } } | null } | null };
|
||||
|
||||
export type GetPendingProjectAccessRequestsQueryVariables = Exact<{
|
||||
projectId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetPendingProjectAccessRequestsQuery = { __typename?: 'Query', project: { __typename?: 'Project', id: string, name: string, pendingAccessRequests?: Array<{ __typename?: 'ProjectAccessRequest', id: string, requesterId: string, projectId: string, createdAt: string, project: { __typename?: 'Project', id: string, name: string }, requester: { __typename?: 'LimitedUser', id: string, name: string } }> | null } };
|
||||
|
||||
export type UseProjectAccessRequestMutationVariables = Exact<{
|
||||
requestId: Scalars['String']['input'];
|
||||
accept: Scalars['Boolean']['input'];
|
||||
role?: StreamRole;
|
||||
}>;
|
||||
|
||||
|
||||
export type UseProjectAccessRequestMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', accessRequestMutations: { __typename?: 'ProjectAccessRequestMutations', use: { __typename?: 'Project', id: string } } } };
|
||||
|
||||
export type CreateProjectCommentMutationVariables = Exact<{
|
||||
input: CreateCommentInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateProjectCommentMutation = { __typename?: 'Mutation', commentMutations: { __typename?: 'CommentMutations', create: { __typename?: 'Comment', id: string, rawText: string, authorId: string, text: { __typename?: 'SmartTextEditorValue', doc?: Record<string, unknown> | null } } } };
|
||||
|
||||
export type BasicProjectFieldsFragment = { __typename?: 'Project', id: string, name: string, description?: string | null, visibility: ProjectVisibility, allowPublicComments: boolean, role?: string | null, createdAt: string, updatedAt: string };
|
||||
|
||||
export type AdminProjectListQueryVariables = Exact<{
|
||||
|
@ -3727,6 +3891,28 @@ export type AdminProjectListQueryVariables = Exact<{
|
|||
|
||||
export type AdminProjectListQuery = { __typename?: 'Query', admin: { __typename?: 'AdminQueries', projectList: { __typename?: 'ProjectCollection', cursor?: string | null, totalCount: number, items: Array<{ __typename?: 'Project', id: string, name: string, description?: string | null, visibility: ProjectVisibility, allowPublicComments: boolean, role?: string | null, createdAt: string, updatedAt: string }> } } };
|
||||
|
||||
export type GetProjectObjectQueryVariables = Exact<{
|
||||
projectId: Scalars['String']['input'];
|
||||
objectId: Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type GetProjectObjectQuery = { __typename?: 'Query', project: { __typename?: 'Project', object?: { __typename?: 'Object', id: string, createdAt?: string | null } | null } };
|
||||
|
||||
export type CreateProjectMutationVariables = Exact<{
|
||||
input: ProjectCreateInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateProjectMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', create: { __typename?: 'Project', id: string, name: string, description?: string | null, visibility: ProjectVisibility, allowPublicComments: boolean, role?: string | null, createdAt: string, updatedAt: string } } };
|
||||
|
||||
export type BatchDeleteProjectsMutationVariables = Exact<{
|
||||
ids: Array<Scalars['String']['input']> | Scalars['String']['input'];
|
||||
}>;
|
||||
|
||||
|
||||
export type BatchDeleteProjectsMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', batchDelete: boolean } };
|
||||
|
||||
export type CreateServerInviteMutationVariables = Exact<{
|
||||
input: ServerInviteCreateInput;
|
||||
}>;
|
||||
|
@ -3907,11 +4093,26 @@ export type RequestVerificationMutationVariables = Exact<{ [key: string]: never;
|
|||
|
||||
export type RequestVerificationMutation = { __typename?: 'Mutation', requestVerification: boolean };
|
||||
|
||||
export type CreateProjectVersionMutationVariables = Exact<{
|
||||
input: CreateVersionInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateProjectVersionMutation = { __typename?: 'Mutation', versionMutations: { __typename?: 'VersionMutations', create: { __typename?: 'Version', id: string, message?: string | null, sourceApplication?: string | null, referencedObject: string, model: { __typename?: 'Model', id: string } } } };
|
||||
|
||||
export type MarkProjectVersionReceivedMutationVariables = Exact<{
|
||||
input: MarkReceivedVersionInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type MarkProjectVersionReceivedMutation = { __typename?: 'Mutation', versionMutations: { __typename?: 'VersionMutations', markReceived: boolean } };
|
||||
|
||||
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<BasicStreamAccessRequestFieldsFragment, unknown>;
|
||||
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<TestAutomateFunctionFragment, unknown>;
|
||||
export const TestAutomationFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestAutomation"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Automation"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"runs"},"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":"trigger"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"VersionCreatedTrigger"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"elapsed"}},{"kind":"Field","name":{"kind":"Name","value":"results"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"currentRevision"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"triggerDefinitions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"VersionCreatedTriggerDefinition"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"functions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"parameters"}},{"kind":"Field","name":{"kind":"Name","value":"release"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"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"}}]}}]}}]}}]}}]} as unknown as DocumentNode<TestAutomationFragment, unknown>;
|
||||
export const CommentWithRepliesFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CommentWithReplies"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Comment"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"rawText"}},{"kind":"Field","name":{"kind":"Name","value":"text"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"doc"}},{"kind":"Field","name":{"kind":"Name","value":"attachments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"replies"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}],"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":"text"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"doc"}},{"kind":"Field","name":{"kind":"Name","value":"attachments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<CommentWithRepliesFragment, unknown>;
|
||||
export const BaseCommitFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseCommitFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Commit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"streamName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"commentCount"}}]}}]} as unknown as DocumentNode<BaseCommitFieldsFragment, unknown>;
|
||||
export const BasicProjectAccessRequestFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"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":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<BasicProjectAccessRequestFieldsFragment, unknown>;
|
||||
export const BasicProjectFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<BasicProjectFieldsFragment, unknown>;
|
||||
export const StreamInviteDataFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"StreamInviteData"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingStreamCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"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":"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":"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":"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<StreamInviteDataFragment, unknown>;
|
||||
export const BasicStreamFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicStreamFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Stream"}},"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":"isPublic"}},{"kind":"Field","name":{"kind":"Name","value":"isDiscoverable"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<BasicStreamFieldsFragment, unknown>;
|
||||
|
@ -3938,7 +4139,18 @@ export const ReadOtherUsersCommitsDocument = {"kind":"Document","definitions":[{
|
|||
export const ReadStreamBranchCommitsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ReadStreamBranchCommits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"branch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}}}],"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":"commits"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BaseCommitFields"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseCommitFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Commit"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"authorName"}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}},{"kind":"Field","name":{"kind":"Name","value":"authorAvatar"}},{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"streamName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceApplication"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"referencedObject"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"commentCount"}}]}}]} as unknown as DocumentNode<ReadStreamBranchCommitsQuery, ReadStreamBranchCommitsQueryVariables>;
|
||||
export const MoveCommitsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MoveCommits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CommitsMoveInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commitsMove"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<MoveCommitsMutation, MoveCommitsMutationVariables>;
|
||||
export const DeleteCommitsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteCommits"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CommitsDeleteInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commitsDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<DeleteCommitsMutation, DeleteCommitsMutationVariables>;
|
||||
export const CreateProjectModelDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProjectModel"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateModelInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelMutations"},"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode<CreateProjectModelMutation, CreateProjectModelMutationVariables>;
|
||||
export const FindProjectModelByNameDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindProjectModelByName"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelByName"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}}],"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<FindProjectModelByNameQuery, FindProjectModelByNameQueryVariables>;
|
||||
export const CreateProjectAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProjectAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accessRequestMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"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":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<CreateProjectAccessRequestMutation, CreateProjectAccessRequestMutationVariables>;
|
||||
export const GetActiveUserProjectAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserProjectAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"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":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<GetActiveUserProjectAccessRequestQuery, GetActiveUserProjectAccessRequestQueryVariables>;
|
||||
export const GetActiveUserFullProjectAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserFullProjectAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"}},{"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"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"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":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<GetActiveUserFullProjectAccessRequestQuery, GetActiveUserFullProjectAccessRequestQueryVariables>;
|
||||
export const GetPendingProjectAccessRequestsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPendingProjectAccessRequests"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"pendingAccessRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"}},{"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"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"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":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode<GetPendingProjectAccessRequestsQuery, GetPendingProjectAccessRequestsQueryVariables>;
|
||||
export const UseProjectAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UseProjectAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"requestId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"accept"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"role"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StreamRole"}}},"defaultValue":{"kind":"EnumValue","value":"STREAM_CONTRIBUTOR"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accessRequestMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"use"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"requestId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"requestId"}}},{"kind":"Argument","name":{"kind":"Name","value":"accept"},"value":{"kind":"Variable","name":{"kind":"Name","value":"accept"}}},{"kind":"Argument","name":{"kind":"Name","value":"role"},"value":{"kind":"Variable","name":{"kind":"Name","value":"role"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UseProjectAccessRequestMutation, UseProjectAccessRequestMutationVariables>;
|
||||
export const CreateProjectCommentDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProjectComment"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateCommentInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commentMutations"},"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":"rawText"}},{"kind":"Field","name":{"kind":"Name","value":"text"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"doc"}}]}},{"kind":"Field","name":{"kind":"Name","value":"authorId"}}]}}]}}]}}]} as unknown as DocumentNode<CreateProjectCommentMutation, CreateProjectCommentMutationVariables>;
|
||||
export const AdminProjectListDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AdminProjectList"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"visibility"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"25"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"admin"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectList"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}},{"kind":"Argument","name":{"kind":"Name","value":"visibility"},"value":{"kind":"Variable","name":{"kind":"Name","value":"visibility"}}},{"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<AdminProjectListQuery, AdminProjectListQueryVariables>;
|
||||
export const GetProjectObjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetProjectObject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"object"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode<GetProjectObjectQuery, GetProjectObjectQueryVariables>;
|
||||
export const CreateProjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProject"},"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":"BasicProjectFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"allowPublicComments"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<CreateProjectMutation, CreateProjectMutationVariables>;
|
||||
export const BatchDeleteProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"BatchDeleteProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"ids"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"batchDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"ids"},"value":{"kind":"Variable","name":{"kind":"Name","value":"ids"}}}]}]}}]}}]} as unknown as DocumentNode<BatchDeleteProjectsMutation, BatchDeleteProjectsMutationVariables>;
|
||||
export const CreateServerInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateServerInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInviteCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInviteCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<CreateServerInviteMutation, CreateServerInviteMutationVariables>;
|
||||
export const CreateStreamInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateStreamInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"StreamInviteCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamInviteCreate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<CreateStreamInviteMutation, CreateStreamInviteMutationVariables>;
|
||||
export const ResendInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ResendInvite"},"variableDefinitions":[{"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":"inviteResend"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"inviteId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"inviteId"}}}]}]}}]} as unknown as DocumentNode<ResendInviteMutation, ResendInviteMutationVariables>;
|
||||
|
@ -3962,4 +4174,6 @@ export const GetActiveUserDocument = {"kind":"Document","definitions":[{"kind":"
|
|||
export const GetOtherUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOtherUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"otherUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BaseLimitedUserFields"}}]}}]}},{"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<GetOtherUserQuery, GetOtherUserQueryVariables>;
|
||||
export const GetAdminUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAdminUsers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"25"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"0"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"adminUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}},{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}}],"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":"registeredUser"},"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":"invitedUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"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"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetAdminUsersQuery, GetAdminUsersQueryVariables>;
|
||||
export const GetPendingEmailVerificationStatusDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPendingEmailVerificationStatus"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"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":"hasPendingVerification"}}]}}]}}]} as unknown as DocumentNode<GetPendingEmailVerificationStatusQuery, GetPendingEmailVerificationStatusQueryVariables>;
|
||||
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<RequestVerificationMutation, RequestVerificationMutationVariables>;
|
||||
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<RequestVerificationMutation, RequestVerificationMutationVariables>;
|
||||
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<CreateProjectVersionMutation, CreateProjectVersionMutationVariables>;
|
||||
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<MarkProjectVersionReceivedMutation, MarkProjectVersionReceivedMutationVariables>;
|
|
@ -0,0 +1,25 @@
|
|||
import { gql } from 'apollo-server-express'
|
||||
|
||||
export const createProjectModelQuery = gql`
|
||||
mutation CreateProjectModel($input: CreateModelInput!) {
|
||||
modelMutations {
|
||||
create(input: $input) {
|
||||
id
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const findProjectModelByNameQuery = gql`
|
||||
query FindProjectModelByName($projectId: String!, $name: String!) {
|
||||
project(id: $projectId) {
|
||||
modelByName(name: $name) {
|
||||
id
|
||||
name
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
|
@ -0,0 +1,90 @@
|
|||
import { gql } from 'apollo-server-express'
|
||||
|
||||
const basicProjectAccessRequestFragment = gql`
|
||||
fragment BasicProjectAccessRequestFields on ProjectAccessRequest {
|
||||
id
|
||||
requester {
|
||||
id
|
||||
name
|
||||
}
|
||||
requesterId
|
||||
projectId
|
||||
createdAt
|
||||
}
|
||||
`
|
||||
|
||||
export const createProjectAccessRequestMutation = gql`
|
||||
mutation CreateProjectAccessRequest($projectId: String!) {
|
||||
projectMutations {
|
||||
accessRequestMutations {
|
||||
create(projectId: $projectId) {
|
||||
...BasicProjectAccessRequestFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${basicProjectAccessRequestFragment}
|
||||
`
|
||||
|
||||
export const getActiveUserProjectAccessRequestQuery = gql`
|
||||
query GetActiveUserProjectAccessRequest($projectId: String!) {
|
||||
activeUser {
|
||||
projectAccessRequest(projectId: $projectId) {
|
||||
...BasicProjectAccessRequestFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${basicProjectAccessRequestFragment}
|
||||
`
|
||||
|
||||
export const getActiveUserFullProjectAccessRequestQuery = gql`
|
||||
query GetActiveUserFullProjectAccessRequest($projectId: String!) {
|
||||
activeUser {
|
||||
projectAccessRequest(projectId: $projectId) {
|
||||
...BasicProjectAccessRequestFields
|
||||
project {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${basicProjectAccessRequestFragment}
|
||||
`
|
||||
|
||||
export const getPendingProjectAccessRequestsQuery = gql`
|
||||
query GetPendingProjectAccessRequests($projectId: String!) {
|
||||
project(id: $projectId) {
|
||||
id
|
||||
name
|
||||
pendingAccessRequests {
|
||||
...BasicProjectAccessRequestFields
|
||||
project {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${basicProjectAccessRequestFragment}
|
||||
`
|
||||
|
||||
export const useProjectAccessRequestMutation = gql`
|
||||
mutation UseProjectAccessRequest(
|
||||
$requestId: String!
|
||||
$accept: Boolean!
|
||||
$role: StreamRole! = STREAM_CONTRIBUTOR
|
||||
) {
|
||||
projectMutations {
|
||||
accessRequestMutations {
|
||||
use(requestId: $requestId, accept: $accept, role: $role) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
|
@ -0,0 +1,16 @@
|
|||
import { gql } from 'apollo-server-express'
|
||||
|
||||
export const createProjectCommentMutation = gql`
|
||||
mutation CreateProjectComment($input: CreateCommentInput!) {
|
||||
commentMutations {
|
||||
create(input: $input) {
|
||||
id
|
||||
rawText
|
||||
text {
|
||||
doc
|
||||
}
|
||||
authorId
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
|
@ -13,15 +13,7 @@ export const basicProjectFieldsFragment = gql`
|
|||
}
|
||||
`
|
||||
|
||||
/**
|
||||
* query: String
|
||||
orderBy: String
|
||||
visibility: String
|
||||
limit: Int! = 25
|
||||
cursor: String = null
|
||||
*/
|
||||
|
||||
export const adminProjectList = gql`
|
||||
export const adminProjectListQuery = gql`
|
||||
query AdminProjectList(
|
||||
$query: String
|
||||
$orderBy: String
|
||||
|
@ -48,3 +40,34 @@ export const adminProjectList = gql`
|
|||
|
||||
${basicProjectFieldsFragment}
|
||||
`
|
||||
|
||||
export const getProjectObjectQuery = gql`
|
||||
query GetProjectObject($projectId: String!, $objectId: String!) {
|
||||
project(id: $projectId) {
|
||||
object(id: $objectId) {
|
||||
id
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const createProjectMutation = gql`
|
||||
mutation CreateProject($input: ProjectCreateInput!) {
|
||||
projectMutations {
|
||||
create(input: $input) {
|
||||
...BasicProjectFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${basicProjectFieldsFragment}
|
||||
`
|
||||
|
||||
export const batchDeleteProjectsMutation = gql`
|
||||
mutation BatchDeleteProjects($ids: [String!]!) {
|
||||
projectMutations {
|
||||
batchDelete(ids: $ids)
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { gql } from 'apollo-server-express'
|
||||
|
||||
export const createProjectVersionMutation = gql`
|
||||
mutation CreateProjectVersion($input: CreateVersionInput!) {
|
||||
versionMutations {
|
||||
create(input: $input) {
|
||||
id
|
||||
message
|
||||
sourceApplication
|
||||
model {
|
||||
id
|
||||
}
|
||||
referencedObject
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const markProjectVersionReceivedMutation = gql`
|
||||
mutation MarkProjectVersionReceived($input: MarkReceivedVersionInput!) {
|
||||
versionMutations {
|
||||
markReceived(input: $input)
|
||||
}
|
||||
}
|
||||
`
|
|
@ -7,6 +7,8 @@ import { TypedDocumentNode } from '@graphql-typed-document-node/core'
|
|||
import { buildApolloServer } from '@/app'
|
||||
import { addLoadersToCtx } from '@/modules/shared/middleware'
|
||||
import { buildUnauthenticatedApolloServer } from '@/test/serverHelper'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import { AllScopes } from '@speckle/shared'
|
||||
|
||||
type TypedGraphqlResponse<R = Record<string, any>> = GraphQLResponse & {
|
||||
data: Nullable<R>
|
||||
|
@ -51,10 +53,26 @@ export const createTestContext = (
|
|||
/**
|
||||
* Utilities that make it easier to test against an Apollo Server instance
|
||||
*/
|
||||
export const testApolloServer = async (params?: { context?: GraphQLContext }) => {
|
||||
const instance = params?.context
|
||||
export const testApolloServer = async (params?: {
|
||||
context?: GraphQLContext
|
||||
/**
|
||||
* If set, will create an authed context w/ all scopes and Server.User role for thies user id
|
||||
*/
|
||||
authUserId?: string
|
||||
}) => {
|
||||
const initialCtx: GraphQLContext | undefined = params?.authUserId
|
||||
? createTestContext({
|
||||
auth: true,
|
||||
userId: params.authUserId,
|
||||
role: Roles.Server.User,
|
||||
token: 'asd',
|
||||
scopes: AllScopes
|
||||
})
|
||||
: params?.context
|
||||
|
||||
const instance = initialCtx
|
||||
? await buildApolloServer({
|
||||
context: params.context
|
||||
context: initialCtx
|
||||
})
|
||||
: await buildUnauthenticatedApolloServer()
|
||||
|
||||
|
@ -77,7 +95,7 @@ export const testApolloServer = async (params?: { context?: GraphQLContext }) =>
|
|||
const realInstance = options?.context
|
||||
? await buildApolloServer({
|
||||
context: createTestContext({
|
||||
...(params?.context || {}),
|
||||
...(initialCtx || {}),
|
||||
...options.context
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { createBranch } from '@/modules/core/services/branches'
|
||||
import { BasicTestUser } from '@/test/authHelper'
|
||||
import { BasicTestStream } from '@/test/speckle-helpers/streamHelper'
|
||||
import { omit } from 'lodash'
|
||||
|
||||
export type BasicTestBranch = {
|
||||
name: string
|
||||
description?: string
|
||||
/**
|
||||
* The ID of the stream. Will be filled in by createTestBranch().
|
||||
*/
|
||||
streamId: string
|
||||
/**
|
||||
* The ID of the owner. Will be filled in by createTestBranch().
|
||||
*/
|
||||
authorId: string
|
||||
|
||||
/**
|
||||
* The ID of the branch. Will be filled in by createTestBranch().
|
||||
*/
|
||||
id: string
|
||||
}
|
||||
|
||||
export async function createTestBranch(params: {
|
||||
branch: BasicTestBranch
|
||||
stream: BasicTestStream
|
||||
owner: BasicTestUser
|
||||
}) {
|
||||
const { branch, stream, owner } = params
|
||||
branch.streamId = stream.id
|
||||
branch.authorId = owner.id
|
||||
|
||||
const id = await createBranch({
|
||||
...omit(branch, ['id']),
|
||||
description: branch.description || null
|
||||
})
|
||||
branch.id = id
|
||||
}
|
||||
|
||||
export async function createTestBranches(
|
||||
branches: Array<Parameters<typeof createTestBranch>[0]>
|
||||
) {
|
||||
await Promise.all(branches.map((p) => createTestBranch(p)))
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import { createCommitByBranchName } from '@/modules/core/services/commits'
|
||||
import { createObject } from '@/modules/core/services/objects'
|
||||
import { BasicTestUser } from '@/test/authHelper'
|
||||
import { BasicTestStream } from '@/test/speckle-helpers/streamHelper'
|
||||
|
||||
export type BasicTestCommit = {
|
||||
/**
|
||||
|
@ -10,7 +12,13 @@ export type BasicTestCommit = {
|
|||
* Can be left empty, will be filled on creation
|
||||
*/
|
||||
objectId: string
|
||||
/**
|
||||
* Can be left empty, will be filled on creation if stream passed in
|
||||
*/
|
||||
streamId: string
|
||||
/**
|
||||
* Can be left empty, will be filled on creation if owner passed in
|
||||
*/
|
||||
authorId: string
|
||||
/**
|
||||
* Defaults to 'main'
|
||||
|
@ -27,6 +35,10 @@ export type BasicTestCommit = {
|
|||
parents?: string[]
|
||||
}
|
||||
|
||||
export async function createTestObject(params: { projectId: string }) {
|
||||
return await createObject(params.projectId, { foo: 'bar' })
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure all commits have objectId set
|
||||
*/
|
||||
|
@ -42,13 +54,23 @@ async function ensureObjects(commits: BasicTestCommit[]) {
|
|||
/**
|
||||
* Create test commits
|
||||
*/
|
||||
export async function createTestCommits(commits: BasicTestCommit[]) {
|
||||
export async function createTestCommits(
|
||||
commits: BasicTestCommit[],
|
||||
options?: Partial<{ owner: BasicTestUser; stream: BasicTestStream }>
|
||||
) {
|
||||
const { owner, stream } = options || {}
|
||||
|
||||
commits.forEach((c) => {
|
||||
if (owner) c.authorId = owner.id
|
||||
if (stream) c.streamId = stream.id
|
||||
})
|
||||
|
||||
await ensureObjects(commits)
|
||||
await Promise.all(
|
||||
commits.map((c) =>
|
||||
createCommitByBranchName({
|
||||
streamId: c.streamId,
|
||||
branchName: 'main',
|
||||
branchName: c.branchName || 'main',
|
||||
message: c.message || 'this message is auto generated',
|
||||
sourceApplication: 'tests',
|
||||
objectId: c.objectId,
|
||||
|
@ -60,6 +82,9 @@ export async function createTestCommits(commits: BasicTestCommit[]) {
|
|||
)
|
||||
}
|
||||
|
||||
export async function createTestCommit(commit: BasicTestCommit) {
|
||||
await createTestCommits([commit])
|
||||
export async function createTestCommit(
|
||||
commit: BasicTestCommit,
|
||||
options?: Partial<{ owner: BasicTestUser; stream: BasicTestStream }>
|
||||
) {
|
||||
await createTestCommits([commit], options)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export type BasicTestStream = {
|
|||
name: string
|
||||
isPublic: boolean
|
||||
/**
|
||||
* The ID of the owner user
|
||||
* The ID of the owner user. Will be filled in by createTestStream().
|
||||
*/
|
||||
ownerId: string
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { onKeyDown } from '@vueuse/core'
|
||||
import { useMagicKeys, whenever } from '@vueuse/core'
|
||||
import { OperatingSystem } from '@speckle/shared'
|
||||
import { clientOs, ModifierKeys } from '~~/src/helpers/form/input'
|
||||
import { computed, ref } from 'vue'
|
||||
|
@ -9,36 +9,27 @@ import type { Ref } from 'vue'
|
|||
*/
|
||||
export function onKeyboardShortcut(
|
||||
modifiers: ModifierKeys[],
|
||||
...args: Parameters<typeof onKeyDown>
|
||||
key: string,
|
||||
callback: () => void
|
||||
) {
|
||||
onKeyDown(
|
||||
args[0],
|
||||
(e) => {
|
||||
const isAltOrOpt = e.getModifierState('Alt')
|
||||
const isCtrlOrCmd =
|
||||
clientOs === OperatingSystem.Mac
|
||||
? e.getModifierState('Meta')
|
||||
: e.getModifierState('Control')
|
||||
const isShift = e.getModifierState('Shift')
|
||||
const keys = useMagicKeys()
|
||||
|
||||
for (const modifier of modifiers) {
|
||||
switch (modifier) {
|
||||
case ModifierKeys.CtrlOrCmd:
|
||||
if (!isCtrlOrCmd) return
|
||||
break
|
||||
case ModifierKeys.AltOrOpt:
|
||||
if (!isAltOrOpt) return
|
||||
break
|
||||
case ModifierKeys.Shift:
|
||||
if (!isShift) return
|
||||
break
|
||||
}
|
||||
}
|
||||
const modifierKeys = modifiers.map((modifier) => {
|
||||
switch (modifier) {
|
||||
case ModifierKeys.CtrlOrCmd:
|
||||
return clientOs === OperatingSystem.Mac ? 'Meta' : 'Control'
|
||||
case ModifierKeys.AltOrOpt:
|
||||
return 'Alt'
|
||||
case ModifierKeys.Shift:
|
||||
return 'Shift'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
args[1](e)
|
||||
},
|
||||
args[2]
|
||||
)
|
||||
const keyCombination = `${modifierKeys.join('+')}+${key}`
|
||||
|
||||
whenever(keys[keyCombination], callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче