feat: add missing FE1 fields to the FE2 GQL schema (#2471)

* WIP version create

* commitCreate migrated

* minor cleanup

* commitReceived migrated

* added Project.object

* Project.comment introduced

* moving away old API usages in FE1

* ProjectMutations.batchDelete

* project pending access requests

* WIP project access req tests

* project access req tests done

* ModelByName test

* version mutation tests

* project.object tests

* batch delete tests

* minor improvements to redirect logging
This commit is contained in:
Kristaps Fabians Geikins
2024-07-10 12:33:53 +03:00
committed by GitHub
parent e703bb7415
commit 0b2ca9a515
52 changed files with 2359 additions and 285 deletions
+1 -1
View File
@@ -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',
@@ -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
>
@@ -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.
*/
File diff suppressed because one or more lines are too long
@@ -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) || []
+6 -3
View File
@@ -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."
)
"""
+3
View File
@@ -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'
@@ -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;
};
+1 -1
View File
@@ -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,
+228 -14
View File
@@ -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>;
+25
View File
@@ -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
}
}
}
`
+32 -9
View File
@@ -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)
}
}
`
+25
View File
@@ -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)
}
}
`
+22 -4
View File
@@ -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
/**