chore(server): core IoC #82 - (most) branches resolvers to TS

This commit is contained in:
Kristaps Fabians Geikins
2024-10-17 13:23:13 +03:00
parent 93d0ffd31b
commit 0d2fb7db9b
5 changed files with 130 additions and 127 deletions
+1
View File
@@ -72,6 +72,7 @@ generates:
ServerApp: '@/modules/auth/helpers/graphTypes#ServerAppGraphQLReturn'
ServerAppListItem: '@/modules/auth/helpers/graphTypes#ServerAppListItemGraphQLReturn'
ServerInfo: '@/modules/core/helpers/graphTypes#ServerInfoGraphQLReturn'
Branch: '@/modules/core/helpers/graphTypes#BranchGraphQLReturn'
modules/cross-server-sync/graph/generated/graphql.ts:
plugins:
- 'typescript'
@@ -1,5 +1,5 @@
import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
import { StreamGraphQLReturn, CommitGraphQLReturn, ProjectGraphQLReturn, ObjectGraphQLReturn, VersionGraphQLReturn, ServerInviteGraphQLReturnType, ModelGraphQLReturn, ModelsTreeItemGraphQLReturn, MutationsObjectGraphQLReturn, LimitedUserGraphQLReturn, UserGraphQLReturn, GraphQLEmptyReturn, StreamCollaboratorGraphQLReturn, ServerInfoGraphQLReturn } from '@/modules/core/helpers/graphTypes';
import { StreamGraphQLReturn, CommitGraphQLReturn, ProjectGraphQLReturn, ObjectGraphQLReturn, VersionGraphQLReturn, ServerInviteGraphQLReturnType, ModelGraphQLReturn, ModelsTreeItemGraphQLReturn, MutationsObjectGraphQLReturn, LimitedUserGraphQLReturn, UserGraphQLReturn, GraphQLEmptyReturn, StreamCollaboratorGraphQLReturn, ServerInfoGraphQLReturn, BranchGraphQLReturn } 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';
@@ -4339,7 +4339,7 @@ export type ResolversTypes = {
BlobMetadata: ResolverTypeWrapper<BlobStorageItem>;
BlobMetadataCollection: ResolverTypeWrapper<Omit<BlobMetadataCollection, 'items'> & { items?: Maybe<Array<ResolversTypes['BlobMetadata']>> }>;
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
Branch: ResolverTypeWrapper<Omit<Branch, 'activity' | 'author' | 'commits'> & { activity?: Maybe<ResolversTypes['ActivityCollection']>, author?: Maybe<ResolversTypes['User']>, commits?: Maybe<ResolversTypes['CommitCollection']> }>;
Branch: ResolverTypeWrapper<BranchGraphQLReturn>;
BranchCollection: ResolverTypeWrapper<Omit<BranchCollection, 'items'> & { items?: Maybe<Array<ResolversTypes['Branch']>> }>;
BranchCreateInput: BranchCreateInput;
BranchDeleteInput: BranchDeleteInput;
@@ -4599,7 +4599,7 @@ export type ResolversParentTypes = {
BlobMetadata: BlobStorageItem;
BlobMetadataCollection: Omit<BlobMetadataCollection, 'items'> & { items?: Maybe<Array<ResolversParentTypes['BlobMetadata']>> };
Boolean: Scalars['Boolean']['output'];
Branch: Omit<Branch, 'activity' | 'author' | 'commits'> & { activity?: Maybe<ResolversParentTypes['ActivityCollection']>, author?: Maybe<ResolversParentTypes['User']>, commits?: Maybe<ResolversParentTypes['CommitCollection']> };
Branch: BranchGraphQLReturn;
BranchCollection: Omit<BranchCollection, 'items'> & { items?: Maybe<Array<ResolversParentTypes['Branch']>> };
BranchCreateInput: BranchCreateInput;
BranchDeleteInput: BranchDeleteInput;
@@ -1,142 +1,18 @@
'use strict'
const { withFilter } = require('graphql-subscriptions')
const {
pubsub,
BranchSubscriptions: BranchPubsubEvents
} = require('@/modules/shared/utils/subscriptions')
const { authorizeResolver } = require('@/modules/shared')
const {
createBranchAndNotifyFactory,
updateBranchAndNotifyFactory,
deleteBranchAndNotifyFactory
} = require('@/modules/core/services/branch/management')
const {
getPaginatedStreamBranches
} = require('@/modules/core/services/branch/retrieval')
const { Roles } = require('@speckle/shared')
const {
getBranchByIdFactory,
getStreamBranchByNameFactory,
createBranchFactory,
updateBranchFactory,
deleteBranchByIdFactory
} = require('@/modules/core/repositories/branches')
const { db } = require('@/db/knex')
const {
addBranchCreatedActivity,
addBranchUpdatedActivity,
addBranchDeletedActivity
} = require('@/modules/activitystream/services/branchActivity')
const {
getStreamFactory,
markBranchStreamUpdatedFactory
} = require('@/modules/core/repositories/streams')
const { ModelsEmitter } = require('@/modules/core/events/modelsEmitter')
const { legacyGetUserFactory } = require('@/modules/core/repositories/users')
// subscription events
const BRANCH_CREATED = BranchPubsubEvents.BranchCreated
const BRANCH_UPDATED = BranchPubsubEvents.BranchUpdated
const BRANCH_DELETED = BranchPubsubEvents.BranchDeleted
const markBranchStreamUpdated = markBranchStreamUpdatedFactory({ db })
const getStream = getStreamFactory({ db })
const getBranchById = getBranchByIdFactory({ db })
const getStreamBranchByName = getStreamBranchByNameFactory({ db })
const createBranchAndNotify = createBranchAndNotifyFactory({
getStreamBranchByName,
createBranch: createBranchFactory({ db }),
addBranchCreatedActivity
})
const updateBranchAndNotify = updateBranchAndNotifyFactory({
getBranchById,
updateBranch: updateBranchFactory({ db }),
addBranchUpdatedActivity
})
const deleteBranchAndNotify = deleteBranchAndNotifyFactory({
getStream,
getBranchById: getBranchByIdFactory({ db }),
modelsEventsEmitter: ModelsEmitter.emit,
markBranchStreamUpdated,
addBranchDeletedActivity,
deleteBranchById: deleteBranchByIdFactory({ db })
})
const getUser = legacyGetUserFactory({ db })
/** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */
module.exports = {
Query: {},
Stream: {
async branches(parent, args) {
return await getPaginatedStreamBranches(parent.id, args)
},
async branch(parent, args) {
// TODO: TEMPORARY HACK
// Temporary "Forwards" compatibility layer to allow .NET and PY clients
// to use FE2 urls without major changes.
// When getting a branch by name, if not found, we try to do a 'hail mary' attempt
// and get it by id as well (this would be coming from a FE2 url).
const branchByName = await getStreamBranchByName(parent.id, args.name)
if (branchByName) return branchByName
const branchByIdRes = await getBranchById(args.name)
if (!branchByIdRes) return null
// Extra validation to check if it actually belongs to the stream
if (branchByIdRes.streamId !== parent.id) return null
return branchByIdRes
}
},
Branch: {
async author(parent, args, context) {
if (parent.authorId && context.auth) return await getUser(parent.authorId)
else return null
}
},
Mutation: {
async branchCreate(parent, args, context) {
await authorizeResolver(
context.userId,
args.branch.streamId,
Roles.Stream.Contributor,
context.resourceAccessRules
)
const { id } = await createBranchAndNotify(args.branch, context.userId)
return id
},
async branchUpdate(parent, args, context) {
await authorizeResolver(
context.userId,
args.branch.streamId,
Roles.Stream.Contributor,
context.resourceAccessRules
)
const newBranch = await updateBranchAndNotify(args.branch, context.userId)
return !!newBranch
},
async branchDelete(parent, args, context) {
await authorizeResolver(
context.userId,
args.branch.streamId,
Roles.Stream.Contributor,
context.resourceAccessRules
)
const deleted = await deleteBranchAndNotify(args.branch, context.userId)
return deleted
}
},
Subscription: {
branchCreated: {
subscribe: withFilter(
@@ -0,0 +1,124 @@
import { authorizeResolver } from '@/modules/shared'
import {
createBranchAndNotifyFactory,
updateBranchAndNotifyFactory,
deleteBranchAndNotifyFactory
} from '@/modules/core/services/branch/management'
import { getPaginatedStreamBranches } from '@/modules/core/services/branch/retrieval'
import { Roles } from '@speckle/shared'
import {
getBranchByIdFactory,
getStreamBranchByNameFactory,
createBranchFactory,
updateBranchFactory,
deleteBranchByIdFactory
} from '@/modules/core/repositories/branches'
import { db } from '@/db/knex'
import {
addBranchCreatedActivity,
addBranchUpdatedActivity,
addBranchDeletedActivity
} from '@/modules/activitystream/services/branchActivity'
import {
getStreamFactory,
markBranchStreamUpdatedFactory
} from '@/modules/core/repositories/streams'
import { ModelsEmitter } from '@/modules/core/events/modelsEmitter'
import { legacyGetUserFactory } from '@/modules/core/repositories/users'
import { Resolvers } from '@/modules/core/graph/generated/graphql'
const markBranchStreamUpdated = markBranchStreamUpdatedFactory({ db })
const getStream = getStreamFactory({ db })
const getBranchById = getBranchByIdFactory({ db })
const getStreamBranchByName = getStreamBranchByNameFactory({ db })
const createBranchAndNotify = createBranchAndNotifyFactory({
getStreamBranchByName,
createBranch: createBranchFactory({ db }),
addBranchCreatedActivity
})
const updateBranchAndNotify = updateBranchAndNotifyFactory({
getBranchById,
updateBranch: updateBranchFactory({ db }),
addBranchUpdatedActivity
})
const deleteBranchAndNotify = deleteBranchAndNotifyFactory({
getStream,
getBranchById: getBranchByIdFactory({ db }),
modelsEventsEmitter: ModelsEmitter.emit,
markBranchStreamUpdated,
addBranchDeletedActivity,
deleteBranchById: deleteBranchByIdFactory({ db })
})
const getUser = legacyGetUserFactory({ db })
export = {
Query: {},
Stream: {
async branches(parent, args) {
return await getPaginatedStreamBranches(parent.id, args)
},
async branch(parent, args) {
// TODO: TEMPORARY HACK
// Temporary "Forwards" compatibility layer to allow .NET and PY clients
// to use FE2 urls without major changes.
// When getting a branch by name, if not found, we try to do a 'hail mary' attempt
// and get it by id as well (this would be coming from a FE2 url).
const branchByName = await getStreamBranchByName(parent.id, args.name)
if (branchByName) return branchByName
const branchByIdRes = await getBranchById(args.name)
if (!branchByIdRes) return null
// Extra validation to check if it actually belongs to the stream
if (branchByIdRes.streamId !== parent.id) return null
return branchByIdRes
}
},
Branch: {
async author(parent, _args, context) {
if (parent.authorId && context.auth) return await getUser(parent.authorId)
else return null
}
},
Mutation: {
async branchCreate(_parent, args, context) {
await authorizeResolver(
context.userId,
args.branch.streamId,
Roles.Stream.Contributor,
context.resourceAccessRules
)
const { id } = await createBranchAndNotify(args.branch, context.userId!)
return id
},
async branchUpdate(_parent, args, context) {
await authorizeResolver(
context.userId,
args.branch.streamId,
Roles.Stream.Contributor,
context.resourceAccessRules
)
const newBranch = await updateBranchAndNotify(args.branch, context.userId!)
return !!newBranch
},
async branchDelete(_parent, args, context) {
await authorizeResolver(
context.userId,
args.branch.streamId,
Roles.Stream.Contributor,
context.resourceAccessRules
)
const deleted = await deleteBranchAndNotify(args.branch, context.userId!)
return deleted
}
}
} as Resolvers
@@ -40,6 +40,8 @@ export type CommitGraphQLReturn = Commit & {
author: Nullable<string>
}
export type BranchGraphQLReturn = BranchRecord
export type ProjectGraphQLReturn = StreamGraphQLReturn
export type ModelGraphQLReturn = BranchRecord