From 55fe69ce138039f129e60a68a402fb26a19e0266 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 8 Oct 2024 12:26:28 +0300 Subject: [PATCH 001/136] chore(server): core IoC 28 - cloning object repo fns --- .../modules/core/domain/objects/operations.ts | 14 +++++++ .../modules/core/repositories/objects.ts | 41 +++++++++++-------- .../modules/core/services/streams/clone.ts | 11 +++-- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/packages/server/modules/core/domain/objects/operations.ts b/packages/server/modules/core/domain/objects/operations.ts index b72d7656f..da8d79a3a 100644 --- a/packages/server/modules/core/domain/objects/operations.ts +++ b/packages/server/modules/core/domain/objects/operations.ts @@ -1,5 +1,7 @@ import { SpeckleObject } from '@/modules/core/domain/objects/types' +import { BatchedSelectOptions } from '@/modules/shared/helpers/dbHelper' import { Optional } from '@speckle/shared' +import { Knex } from 'knex' export type GetStreamObjects = ( streamId: string, @@ -10,3 +12,15 @@ export type GetObject = ( objectId: string, streamId: string ) => Promise> + +export type GetBatchedStreamObjects = ( + streamId: string, + options?: Partial +) => AsyncGenerator + +export type StoreObjects = ( + objects: SpeckleObject[], + options?: Partial<{ + trx: Knex.Transaction + }> +) => Promise diff --git a/packages/server/modules/core/repositories/objects.ts b/packages/server/modules/core/repositories/objects.ts index cb1959f56..69c1a1c32 100644 --- a/packages/server/modules/core/repositories/objects.ts +++ b/packages/server/modules/core/repositories/objects.ts @@ -6,7 +6,12 @@ import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' import { Knex } from 'knex' -import { GetObject, GetStreamObjects } from '@/modules/core/domain/objects/operations' +import { + GetBatchedStreamObjects, + GetObject, + GetStreamObjects, + StoreObjects +} from '@/modules/core/domain/objects/operations' const tables = { objects: (db: Knex) => db(Objects.name) @@ -35,22 +40,22 @@ export const getObjectFactory = .first() } -export function getBatchedStreamObjects( - streamId: string, - options?: Partial -) { - const baseQuery = Objects.knex() - .where(Objects.col.streamId, streamId) - .orderBy(Objects.col.id) +export const getBatchedStreamObjectsFactory = + (deps: { db: Knex }): GetBatchedStreamObjects => + (streamId: string, options?: Partial) => { + const baseQuery = tables + .objects(deps.db) + .select('*') + .where(Objects.col.streamId, streamId) + .orderBy(Objects.col.id) - return executeBatchedSelect(baseQuery, options) -} + return executeBatchedSelect(baseQuery, options) + } -export async function insertObjects( - objects: ObjectRecord[], - options?: Partial<{ trx: Knex.Transaction }> -) { - const q = Objects.knex().insert(objects) - if (options?.trx) q.transacting(options.trx) - return await q -} +export const insertObjectsFactory = + (deps: { db: Knex }): StoreObjects => + async (objects: ObjectRecord[], options?: Partial<{ trx: Knex.Transaction }>) => { + const q = tables.objects(deps.db).insert(objects) + if (options?.trx) q.transacting(options.trx) + return await q + } diff --git a/packages/server/modules/core/services/streams/clone.ts b/packages/server/modules/core/services/streams/clone.ts index 72bf21301..36ced0461 100644 --- a/packages/server/modules/core/services/streams/clone.ts +++ b/packages/server/modules/core/services/streams/clone.ts @@ -10,10 +10,6 @@ import { StreamWithOptionalRole } from '@/modules/core/repositories/streams' import { getUser, UserWithOptionalRole } from '@/modules/core/repositories/users' -import { - getBatchedStreamObjects, - insertObjects -} from '@/modules/core/repositories/objects' import { generateCommitId, insertStreamCommitsFactory, @@ -41,6 +37,10 @@ import knex, { db } from '@/db/knex' import { Knex } from 'knex' import { InsertCommentPayload } from '@/modules/comments/domain/operations' import { SmartTextEditorValueSchema } from '@/modules/core/services/richTextEditorService' +import { + getBatchedStreamObjectsFactory, + insertObjectsFactory +} from '@/modules/core/repositories/objects' type CloneStreamInitialState = { user: UserWithOptionalRole @@ -119,6 +119,9 @@ async function cloneStreamEntity(state: CloneStreamInitialState) { // eslint-disable-next-line @typescript-eslint/no-unused-vars async function cloneStreamObjects(state: CloneStreamInitialState, newStreamId: string) { const { getNewDate } = incrementingDateGenerator() + const getBatchedStreamObjects = getBatchedStreamObjectsFactory({ db }) + const insertObjects = insertObjectsFactory({ db }) + for await (const objectsBatch of getBatchedStreamObjects(state.targetStream.id, { trx: state.trx })) { From c2c95d20c21c71dfd6922c0484b05a660531db17 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Wed, 9 Oct 2024 09:28:17 +0200 Subject: [PATCH 002/136] Activitystream IoC 1 addStreamCreatedActivity (#3206) * chore(activitystream): addStreamPermissionsAddedActivity refactor multi region * chore(activitystream): addStreamCreatedActivity refactor multi region --- .../activitystream/services/streamActivity.ts | 70 +++++++++++-------- .../core/services/streams/management.ts | 9 ++- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/packages/server/modules/activitystream/services/streamActivity.ts b/packages/server/modules/activitystream/services/streamActivity.ts index f31223eb9..4d240c2dc 100644 --- a/packages/server/modules/activitystream/services/streamActivity.ts +++ b/packages/server/modules/activitystream/services/streamActivity.ts @@ -175,38 +175,46 @@ export async function addStreamClonedActivity( /** * Save "user created stream" activity item */ -export async function addStreamCreatedActivity(params: { - streamId: string - creatorId: string - input: StreamCreateInput | ProjectCreateInput - stream: StreamRecord -}) { - const { streamId, creatorId, input, stream } = params +export const addStreamCreatedActivityFactory = + ({ + saveActivity, + publish + }: { + saveActivity: SaveActivity + publish: PublishSubscription + }) => + async (params: { + streamId: string + creatorId: string + input: StreamCreateInput | ProjectCreateInput + stream: StreamRecord + }) => { + const { streamId, creatorId, input, stream } = params - await Promise.all([ - saveActivityFactory({ db })({ - streamId, - resourceType: ResourceTypes.Stream, - resourceId: streamId, - actionType: ActionTypes.Stream.Create, - userId: creatorId, - info: { input }, - message: `Stream ${input.name} created` - }), - pubsub.publish(StreamPubsubEvents.UserStreamAdded, { - userStreamAdded: { id: streamId, ...input }, - ownerId: creatorId - }), - publish(UserSubscriptions.UserProjectsUpdated, { - userProjectsUpdated: { - id: streamId, - type: UserProjectsUpdatedMessageType.Added, - project: stream - }, - ownerId: creatorId - }) - ]) -} + await Promise.all([ + saveActivity({ + streamId, + resourceType: ResourceTypes.Stream, + resourceId: streamId, + actionType: ActionTypes.Stream.Create, + userId: creatorId, + info: { input }, + message: `Stream ${input.name} created` + }), + publish(StreamPubsubEvents.UserStreamAdded, { + userStreamAdded: { id: streamId, ...input }, + ownerId: creatorId + }), + publish(UserSubscriptions.UserProjectsUpdated, { + userProjectsUpdated: { + id: streamId, + type: UserProjectsUpdatedMessageType.Added, + project: stream + }, + ownerId: creatorId + }) + ]) + } /** * Save "stream permissions granted to user" activity item diff --git a/packages/server/modules/core/services/streams/management.ts b/packages/server/modules/core/services/streams/management.ts index 117ed123b..bef1de143 100644 --- a/packages/server/modules/core/services/streams/management.ts +++ b/packages/server/modules/core/services/streams/management.ts @@ -1,6 +1,6 @@ import { MaybeNullOrUndefined, Roles, wait } from '@speckle/shared' import { - addStreamCreatedActivity, + addStreamCreatedActivityFactory, addStreamDeletedActivity, addStreamUpdatedActivity } from '@/modules/activitystream/services/streamActivity' @@ -55,6 +55,8 @@ import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/ser import { getEventBus } from '@/modules/shared/services/eventBus' import { ProjectInviteResourceType } from '@/modules/serverinvites/domain/constants' import { createBranchFactory } from '@/modules/core/repositories/branches' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' export async function createStreamReturnRecord( params: (StreamCreateInput | ProjectCreateInput) & { @@ -112,7 +114,10 @@ export async function createStreamReturnRecord( // Save activity if (createActivity) { - await addStreamCreatedActivity({ + await addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish + })({ streamId, input: params, stream, From a3fb0d7c0edc1a04b75a7eed153fbc70a5df2120 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Wed, 9 Oct 2024 09:29:24 +0200 Subject: [PATCH 003/136] Activitystream IoC 2 addStreamClonedActivity (#3207) * chore(activitystream): addStreamPermissionsAddedActivity refactor multi region * chore(activitystream): addStreamCreatedActivity refactor multi region * chore(activitystream): addStreamClonedActivity refactor multi region --- .../activitystream/services/streamActivity.ts | 80 ++++++++++--------- .../modules/core/services/streams/clone.ts | 9 ++- 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/packages/server/modules/activitystream/services/streamActivity.ts b/packages/server/modules/activitystream/services/streamActivity.ts index 4d240c2dc..1e3ca7511 100644 --- a/packages/server/modules/activitystream/services/streamActivity.ts +++ b/packages/server/modules/activitystream/services/streamActivity.ts @@ -131,46 +131,54 @@ export async function addStreamDeletedActivity(params: { /** * Save "user cloned stream X" activity item */ -export async function addStreamClonedActivity( - params: { - sourceStreamId: string - newStream: StreamRecord - clonerId: string - }, - options?: Partial<{ trx: Knex.Transaction }> -) { - const { trx } = options || {} - const { sourceStreamId, newStream, clonerId } = params - const newStreamId = newStream.id +export const addStreamClonedActivityFactory = + ({ + saveActivity, + publish + }: { + saveActivity: SaveActivity + publish: PublishSubscription + }) => + async ( + params: { + sourceStreamId: string + newStream: StreamRecord + clonerId: string + }, + options?: Partial<{ trx: Knex.Transaction }> + ) => { + const { trx } = options || {} + const { sourceStreamId, newStream, clonerId } = params + const newStreamId = newStream.id - const publishSubscriptions = async () => - publish(UserSubscriptions.UserProjectsUpdated, { - userProjectsUpdated: { - id: newStreamId, - type: UserProjectsUpdatedMessageType.Added, - project: newStream - }, - ownerId: clonerId - }) + const publishSubscriptions = async () => + publish(UserSubscriptions.UserProjectsUpdated, { + userProjectsUpdated: { + id: newStreamId, + type: UserProjectsUpdatedMessageType.Added, + project: newStream + }, + ownerId: clonerId + }) - await Promise.all([ - saveActivityFactory({ db })({ - streamId: newStreamId, - resourceType: ResourceTypes.Stream, - resourceId: newStreamId, - actionType: ActionTypes.Stream.Clone, - userId: clonerId, - info: { sourceStreamId, newStreamId, clonerId }, - message: `User ${clonerId} cloned stream ${sourceStreamId} as ${newStreamId}` - }), - !trx ? publishSubscriptions() : null - ]) + await Promise.all([ + saveActivity({ + streamId: newStreamId, + resourceType: ResourceTypes.Stream, + resourceId: newStreamId, + actionType: ActionTypes.Stream.Clone, + userId: clonerId, + info: { sourceStreamId, newStreamId, clonerId }, + message: `User ${clonerId} cloned stream ${sourceStreamId} as ${newStreamId}` + }), + !trx ? publishSubscriptions() : null + ]) - if (trx) { - // can't await this, cause it'll block everything - void trx.executionPromise.then(publishSubscriptions) + if (trx) { + // can't await this, cause it'll block everything + void trx.executionPromise.then(publishSubscriptions) + } } -} /** * Save "user created stream" activity item diff --git a/packages/server/modules/core/services/streams/clone.ts b/packages/server/modules/core/services/streams/clone.ts index 72bf21301..d86dcf05c 100644 --- a/packages/server/modules/core/services/streams/clone.ts +++ b/packages/server/modules/core/services/streams/clone.ts @@ -36,11 +36,13 @@ import { insertCommentsFactory } from '@/modules/comments/repositories/comments' import dayjs from 'dayjs' -import { addStreamClonedActivity } from '@/modules/activitystream/services/streamActivity' import knex, { db } from '@/db/knex' import { Knex } from 'knex' import { InsertCommentPayload } from '@/modules/comments/domain/operations' import { SmartTextEditorValueSchema } from '@/modules/core/services/richTextEditorService' +import { addStreamClonedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' type CloneStreamInitialState = { user: UserWithOptionalRole @@ -426,7 +428,10 @@ export async function cloneStream(userId: string, sourceStreamId: string) { // Clone comments await cloneStreamComments(state, coreCloneResult) // Create activity item - await addStreamClonedActivity( + await addStreamClonedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish + })( { sourceStreamId, newStream, From 5b5dd201e2e5bce15a5b1e8035095968b78aa2fc Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 8 Oct 2024 13:50:38 +0300 Subject: [PATCH 004/136] chore(server): IoC 29 - getStream(s)Factory --- .../accessrequests/graph/resolvers/index.ts | 3 +- .../modules/accessrequests/services/stream.ts | 4 +- .../tests/projectAccessRequests.spec.ts | 6 +- .../tests/streamAccessRequests.spec.ts | 6 +- .../server/modules/activitystream/index.ts | 4 +- .../activitystream/repositories/index.ts | 4 +- .../services/serverInvitesActivity.ts | 4 +- .../activitystream/services/summary.ts | 4 +- .../tests/activitySummary.spec.ts | 4 +- .../server/modules/automate/rest/logStream.ts | 4 +- packages/server/modules/blobstorage/index.js | 3 +- .../modules/cli/commands/db/seed/commits.ts | 5 +- .../modules/cli/commands/download/commit.ts | 3 +- .../modules/cli/commands/download/project.ts | 3 +- .../comments/graph/resolvers/comments.ts | 3 +- .../server/modules/comments/services/index.ts | 4 +- .../modules/comments/services/management.ts | 6 +- .../modules/comments/tests/comments.spec.js | 5 +- .../modules/core/domain/streams/operations.ts | 21 ++++ .../modules/core/domain/streams/types.ts | 12 +++ .../modules/core/graph/directives/hasRole.ts | 3 +- .../modules/core/graph/resolvers/branches.js | 5 +- .../modules/core/graph/resolvers/commits.js | 6 +- .../modules/core/graph/resolvers/models.ts | 6 +- .../modules/core/graph/resolvers/projects.ts | 5 +- .../modules/core/graph/resolvers/streams.ts | 6 +- .../modules/core/graph/resolvers/versions.ts | 6 +- packages/server/modules/core/loaders.ts | 5 +- .../modules/core/repositories/streams.ts | 102 +++++++++--------- .../server/modules/core/rest/authUtils.js | 4 +- .../core/services/branch/management.ts | 5 +- .../services/commit/batchCommitActions.ts | 7 +- .../core/services/commit/management.ts | 4 +- .../server/modules/core/services/streams.js | 8 +- .../modules/core/services/streams/clone.ts | 3 +- .../core/services/streams/management.ts | 4 +- .../services/streams/streamAccessService.js | 5 +- .../modules/core/tests/branches.spec.js | 5 +- .../server/modules/core/tests/commits.spec.js | 3 +- .../core/tests/discoverableStreams.spec.ts | 7 +- .../server/modules/core/tests/streams.spec.ts | 3 +- .../server/modules/core/tests/users.spec.js | 12 +-- .../server/modules/cross-server-sync/index.ts | 3 +- .../cross-server-sync/services/commit.ts | 5 +- packages/server/modules/fileuploads/index.ts | 3 +- .../services/handlers/activityDigest.ts | 4 +- .../services/handlers/mentionedInComment.ts | 8 +- .../handlers/newStreamAccessRequest.ts | 7 +- .../handlers/streamAccessRequestApproved.ts | 8 +- packages/server/modules/previews/index.ts | 6 +- .../modules/previews/services/management.ts | 7 +- .../graph/resolvers/serverInvites.ts | 3 +- .../services/coreEmailContents.ts | 4 +- .../services/coreFinalization.ts | 6 +- .../services/coreResourceCollection.ts | 4 +- .../services/projectInviteManagement.ts | 4 +- packages/server/modules/shared/index.ts | 4 +- .../server/modules/shared/services/auth.ts | 4 +- .../modules/webhooks/services/webhooks.ts | 8 +- .../modules/webhooks/tests/webhooks.spec.js | 3 +- .../workspaces/events/eventListener.ts | 7 +- .../workspaces/graph/resolvers/workspaces.ts | 7 +- .../modules/workspaces/services/invites.ts | 4 +- .../workspaces/tests/helpers/creation.ts | 3 +- .../tests/integration/invites.graph.spec.ts | 4 +- .../test/speckle-helpers/inviteHelper.ts | 3 +- 66 files changed, 272 insertions(+), 169 deletions(-) create mode 100644 packages/server/modules/core/domain/streams/operations.ts create mode 100644 packages/server/modules/core/domain/streams/types.ts diff --git a/packages/server/modules/accessrequests/graph/resolvers/index.ts b/packages/server/modules/accessrequests/graph/resolvers/index.ts index 64449caec..35c2fe463 100644 --- a/packages/server/modules/accessrequests/graph/resolvers/index.ts +++ b/packages/server/modules/accessrequests/graph/resolvers/index.ts @@ -20,13 +20,14 @@ import { import { Resolvers } from '@/modules/core/graph/generated/graphql' import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes' import { Roles } from '@/modules/core/helpers/mainConstants' -import { getStream } from '@/modules/core/repositories/streams' +import { getStreamFactory } from '@/modules/core/repositories/streams' import { addOrUpdateStreamCollaborator, validateStreamAccess } from '@/modules/core/services/streams/streamAccessService' import { LogicError } from '@/modules/shared/errors' +const getStream = getStreamFactory({ db }) const getUserProjectAccessRequest = getUserProjectAccessRequestFactory({ getUsersPendingAccessRequest: getUsersPendingAccessRequestFactory({ db }) }) diff --git a/packages/server/modules/accessrequests/services/stream.ts b/packages/server/modules/accessrequests/services/stream.ts index 6ab55757a..d0dcb835b 100644 --- a/packages/server/modules/accessrequests/services/stream.ts +++ b/packages/server/modules/accessrequests/services/stream.ts @@ -13,7 +13,6 @@ import { import { StreamInvalidAccessError } from '@/modules/core/errors/stream' import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types' import { Roles, StreamRoles } from '@/modules/core/helpers/mainConstants' -import { getStream } from '@/modules/core/repositories/streams' import { addOrUpdateStreamCollaborator, validateStreamAccess @@ -35,6 +34,7 @@ import { GetUserStreamAccessRequest, RequestProjectAccess } from '@/modules/accessrequests/domain/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' function buildStreamAccessRequestGraphQLReturn( record: ServerAccessRequestRecord @@ -83,7 +83,7 @@ export const getUserStreamAccessRequestFactory = export const requestProjectAccessFactory = (deps: { getUserStreamAccessRequest: GetUserStreamAccessRequest - getStream: typeof getStream + getStream: GetStream createNewRequest: CreateNewRequest accessRequestsEmitter: (typeof AccessRequestsEmitter)['emit'] }): RequestProjectAccess => diff --git a/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts b/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts index d56558baf..69b003947 100644 --- a/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts +++ b/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts @@ -21,7 +21,10 @@ import { import { StreamAccessUpdateError } from '@/modules/core/errors/stream' import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes' import { Roles } from '@/modules/core/helpers/mainConstants' -import { getStream, getStreamCollaborators } from '@/modules/core/repositories/streams' +import { + getStreamCollaborators, + getStreamFactory +} from '@/modules/core/repositories/streams' import { addOrUpdateStreamCollaborator, removeStreamCollaborator @@ -48,6 +51,7 @@ import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/strea import { expect } from 'chai' import { noop } from 'lodash' +const getStream = getStreamFactory({ db }) const requestProjectAccess = requestProjectAccessFactory({ getUserStreamAccessRequest: getUserStreamAccessRequestFactory({ getUserProjectAccessRequest: getUserProjectAccessRequestFactory({ diff --git a/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts b/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts index 1a061dcfb..c124b16f4 100644 --- a/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts +++ b/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts @@ -23,7 +23,10 @@ import { import { StreamAccessUpdateError } from '@/modules/core/errors/stream' import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes' import { Roles } from '@/modules/core/helpers/mainConstants' -import { getStream, getStreamCollaborators } from '@/modules/core/repositories/streams' +import { + getStreamCollaborators, + getStreamFactory +} from '@/modules/core/repositories/streams' import { addOrUpdateStreamCollaborator, removeStreamCollaborator @@ -50,6 +53,7 @@ import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/strea import { expect } from 'chai' import { noop } from 'lodash' +const getStream = getStreamFactory({ db }) const requestStreamAccess = requestStreamAccessFactory({ requestProjectAccess: requestProjectAccessFactory({ getUserStreamAccessRequest: getUserStreamAccessRequestFactory({ diff --git a/packages/server/modules/activitystream/index.ts b/packages/server/modules/activitystream/index.ts index 36d05c1e2..2581cdf9d 100644 --- a/packages/server/modules/activitystream/index.ts +++ b/packages/server/modules/activitystream/index.ts @@ -6,7 +6,6 @@ import { activitiesLogger, moduleLogger } from '@/logging/logging' import { weeklyEmailDigestEnabled } from '@/modules/shared/helpers/envHelper' import { getEventBus } from '@/modules/shared/services/eventBus' import { handleServerInvitesActivitiesFactory } from '@/modules/activitystream/services/serverInvitesActivity' -import { getStream } from '@/modules/core/repositories/streams' import { sendActivityNotificationsFactory } from '@/modules/activitystream/services/summary' import { getActiveUserStreamsFactory, @@ -15,6 +14,7 @@ import { import { db } from '@/db/knex' import { addStreamInviteSentOutActivityFactory } from '@/modules/activitystream/services/streamActivity' import { publish } from '@/modules/shared/utils/subscriptions' +import { getStreamFactory } from '@/modules/core/repositories/streams' let scheduledTask: ReturnType | null = null let quitEventListeners: Optional> = @@ -24,7 +24,7 @@ const initializeEventListeners = () => { const handleServerInvitesActivities = handleServerInvitesActivitiesFactory({ eventBus: getEventBus(), logger: activitiesLogger, - getStream, + getStream: getStreamFactory({ db }), addStreamInviteSentOutActivity: addStreamInviteSentOutActivityFactory({ saveActivity: saveActivityFactory({ db }), publish diff --git a/packages/server/modules/activitystream/repositories/index.ts b/packages/server/modules/activitystream/repositories/index.ts index 8cfba0b67..e801e052d 100644 --- a/packages/server/modules/activitystream/repositories/index.ts +++ b/packages/server/modules/activitystream/repositories/index.ts @@ -18,12 +18,12 @@ import { import { StreamAcl, StreamActivity } from '@/modules/core/dbSchema' import { Roles } from '@/modules/core/helpers/mainConstants' import { StreamAclRecord } from '@/modules/core/helpers/types' -import { getStream } from '@/modules/core/repositories/streams' import { getServerInfo } from '@/modules/core/services/generic' import { getUser } from '@/modules/core/repositories/users' import { createWebhookEventFactory } from '@/modules/webhooks/repositories/webhooks' import { dispatchStreamEventFactory } from '@/modules/webhooks/services/webhooks' import { Knex } from 'knex' +import { getStreamFactory } from '@/modules/core/repositories/streams' const tables = { streamActivity: (db: Knex) => @@ -253,7 +253,7 @@ export const saveActivityFactory = await dispatchStreamEventFactory({ db, getServerInfo, - getStream, + getStream: getStreamFactory({ db }), createWebhookEvent: createWebhookEventFactory({ db }), getUser })({ diff --git a/packages/server/modules/activitystream/services/serverInvitesActivity.ts b/packages/server/modules/activitystream/services/serverInvitesActivity.ts index d62700807..e004e952d 100644 --- a/packages/server/modules/activitystream/services/serverInvitesActivity.ts +++ b/packages/server/modules/activitystream/services/serverInvitesActivity.ts @@ -7,12 +7,12 @@ import { resolveTarget } from '@/modules/serverinvites/helpers/core' import { EventBus } from '@/modules/shared/services/eventBus' -import { getStream } from '@/modules/core/repositories/streams' import { Logger } from '@/logging/logging' import { AddStreamInviteSentOutActivity } from '@/modules/activitystream/domain/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' type OnServerInviteCreatedFactoryDeps = { - getStream: typeof getStream + getStream: GetStream logger: Logger addStreamInviteSentOutActivity: AddStreamInviteSentOutActivity } diff --git a/packages/server/modules/activitystream/services/summary.ts b/packages/server/modules/activitystream/services/summary.ts index 540099ee6..c2d106848 100644 --- a/packages/server/modules/activitystream/services/summary.ts +++ b/packages/server/modules/activitystream/services/summary.ts @@ -3,19 +3,19 @@ import { NotificationType } from '@/modules/notifications/helpers/types' import { getUser } from '@/modules/core/repositories/users' -import { getStream as getStreamService } from '@/modules/core/services/streams' import { CreateActivitySummary, GetActiveUserStreams, GetActivity } from '@/modules/activitystream/domain/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' export const createActivitySummaryFactory = ({ getStream, getActivity }: { - getStream: typeof getStreamService + getStream: GetStream getActivity: GetActivity }): CreateActivitySummary => async ({ diff --git a/packages/server/modules/activitystream/tests/activitySummary.spec.ts b/packages/server/modules/activitystream/tests/activitySummary.spec.ts index 34a64f46d..34b6ebb72 100644 --- a/packages/server/modules/activitystream/tests/activitySummary.spec.ts +++ b/packages/server/modules/activitystream/tests/activitySummary.spec.ts @@ -6,7 +6,7 @@ import { sendActivityNotificationsFactory } from '@/modules/activitystream/services/summary' import { expect } from 'chai' -import { createStream, deleteStream, getStream } from '@/modules/core/services/streams' +import { createStream, deleteStream } from '@/modules/core/services/streams' import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types' import { ActivityDigestMessage, @@ -19,11 +19,13 @@ import { saveActivityFactory } from '@/modules/activitystream/repositories' import { db } from '@/db/knex' +import { getStreamFactory } from '@/modules/core/repositories/streams' const cleanup = async () => { await truncateTables([StreamActivity.name, Users.name]) } +const getStream = getStreamFactory({ db }) const saveActivity = saveActivityFactory({ db }) const createActivitySummary = createActivitySummaryFactory({ getStream, diff --git a/packages/server/modules/automate/rest/logStream.ts b/packages/server/modules/automate/rest/logStream.ts index 038983056..da5476100 100644 --- a/packages/server/modules/automate/rest/logStream.ts +++ b/packages/server/modules/automate/rest/logStream.ts @@ -6,7 +6,7 @@ import { getAutomationRunWithTokenFactory } from '@/modules/automate/repositories/automations' import { corsMiddleware } from '@/modules/core/configs/cors' -import { getStream } from '@/modules/core/repositories/streams' +import { getStreamFactory } from '@/modules/core/repositories/streams' import { validateRequiredStreamFactory, validateResourceAccess, @@ -29,7 +29,7 @@ export default (app: Application) => { })({ requiredRole: Roles.Server.Guest }), validateScope({ requiredScope: Scopes.Streams.Read }), validateRequiredStreamFactory({ - getStream, + getStream: getStreamFactory({ db }), getAutomationProject: getAutomationProjectFactory({ db }) }), validateStreamRoleBuilderFactory({ getRoles: getRolesFactory({ db }) })({ diff --git a/packages/server/modules/blobstorage/index.js b/packages/server/modules/blobstorage/index.js index 641088a14..996ccd53e 100644 --- a/packages/server/modules/blobstorage/index.js +++ b/packages/server/modules/blobstorage/index.js @@ -41,13 +41,14 @@ const { markUploadOverFileSizeLimitFactory, fullyDeleteBlobFactory } = require('@/modules/blobstorage/services/management') -const { getStream } = require('@/modules/core/repositories/streams') const { getRolesFactory } = require('@/modules/shared/repositories/roles') const { getAutomationProjectFactory } = require('@/modules/automate/repositories/automations') const { adminOverrideEnabled } = require('@/modules/shared/helpers/envHelper') +const { getStreamFactory } = require('@/modules/core/repositories/streams') +const getStream = getStreamFactory({ db }) const getAllStreamBlobIds = getAllStreamBlobIdsFactory({ db }) const updateBlob = updateBlobFactory({ db }) const uploadFileStream = uploadFileStreamFactory({ diff --git a/packages/server/modules/cli/commands/db/seed/commits.ts b/packages/server/modules/cli/commands/db/seed/commits.ts index a8546ff4c..184d6b1a4 100644 --- a/packages/server/modules/cli/commands/db/seed/commits.ts +++ b/packages/server/modules/cli/commands/db/seed/commits.ts @@ -1,5 +1,6 @@ +import { db } from '@/db/knex' import { cliLogger } from '@/logging/logging' -import { getStream } from '@/modules/core/repositories/streams' +import { getStreamFactory } from '@/modules/core/repositories/streams' import { getUser } from '@/modules/core/repositories/users' import { BasicTestCommit, createTestCommits } from '@/test/speckle-helpers/commitHelper' import dayjs from 'dayjs' @@ -28,6 +29,8 @@ const command: CommandModule< } }, handler: async (argv) => { + const getStream = getStreamFactory({ db }) + const count = argv.count const streamId = argv.streamId const authorId = argv.authorId diff --git a/packages/server/modules/cli/commands/download/commit.ts b/packages/server/modules/cli/commands/download/commit.ts index 8698e840d..9cf4bb30f 100644 --- a/packages/server/modules/cli/commands/download/commit.ts +++ b/packages/server/modules/cli/commands/download/commit.ts @@ -2,8 +2,8 @@ import { CommandModule } from 'yargs' import { downloadCommitFactory } from '@/modules/cross-server-sync/services/commit' import { cliLogger } from '@/logging/logging' import { - getStream, getStreamCollaborators, + getStreamFactory, markCommitStreamUpdated } from '@/modules/core/repositories/streams' import { @@ -92,6 +92,7 @@ const command: CommandModule< } }, handler: async (argv) => { + const getStream = getStreamFactory({ db }) const getObject = getObjectFactory({ db }) const getStreamObjects = getStreamObjectsFactory({ db }) const markCommentViewed = markCommentViewedFactory({ db }) diff --git a/packages/server/modules/cli/commands/download/project.ts b/packages/server/modules/cli/commands/download/project.ts index ac2561d2d..35b9bb2b8 100644 --- a/packages/server/modules/cli/commands/download/project.ts +++ b/packages/server/modules/cli/commands/download/project.ts @@ -3,8 +3,8 @@ import { cliLogger } from '@/logging/logging' import { downloadProjectFactory } from '@/modules/cross-server-sync/services/project' import { downloadCommitFactory } from '@/modules/cross-server-sync/services/commit' import { - getStream, getStreamCollaborators, + getStreamFactory, markCommitStreamUpdated } from '@/modules/core/repositories/streams' import { @@ -90,6 +90,7 @@ const command: CommandModule< } }, handler: async (argv) => { + const getStream = getStreamFactory({ db }) const getObject = getObjectFactory({ db }) const getStreamObjects = getStreamObjectsFactory({ db }) diff --git a/packages/server/modules/comments/graph/resolvers/comments.ts b/packages/server/modules/comments/graph/resolvers/comments.ts index efa4d0c2c..a3b2a51ee 100644 --- a/packages/server/modules/comments/graph/resolvers/comments.ts +++ b/packages/server/modules/comments/graph/resolvers/comments.ts @@ -1,6 +1,5 @@ import { pubsub } from '@/modules/shared/utils/subscriptions' import { ForbiddenError } from '@/modules/shared/errors' -import { getStream } from '@/modules/core/services/streams' import { Roles } from '@/modules/core/helpers/mainConstants' import { streamResourceCheckFactory, @@ -96,7 +95,9 @@ import { getStreamBranchesByNameFactory } from '@/modules/core/repositories/branches' import { getStreamObjectsFactory } from '@/modules/core/repositories/objects' +import { getStreamFactory } from '@/modules/core/repositories/streams' +const getStream = getStreamFactory({ db }) const streamResourceCheck = streamResourceCheckFactory({ checkStreamResourceAccess: checkStreamResourceAccessFactory({ db }) }) diff --git a/packages/server/modules/comments/services/index.ts b/packages/server/modules/comments/services/index.ts index 08cbde78b..f90c4aa04 100644 --- a/packages/server/modules/comments/services/index.ts +++ b/packages/server/modules/comments/services/index.ts @@ -24,7 +24,7 @@ import { ValidateInputAttachments } from '@/modules/comments/domain/operations' import { ResourceType } from '@/modules/comments/domain/types' -import { getStream } from '@/modules/core/repositories/streams' +import { GetStream } from '@/modules/core/domain/streams/operations' export const streamResourceCheckFactory = (deps: { @@ -237,7 +237,7 @@ export const editCommentFactory = export const archiveCommentFactory = (deps: { getComment: GetComment - getStream: typeof getStream + getStream: GetStream updateComment: UpdateComment }) => async ({ diff --git a/packages/server/modules/comments/services/management.ts b/packages/server/modules/comments/services/management.ts index aa0a094a8..222368494 100644 --- a/packages/server/modules/comments/services/management.ts +++ b/packages/server/modules/comments/services/management.ts @@ -1,7 +1,6 @@ import { ensureError, Roles, SpeckleViewer } from '@speckle/shared' import { AuthContext } from '@/modules/shared/authz' import { ForbiddenError } from '@/modules/shared/errors' -import { getStream } from '@/modules/core/repositories/streams' import { StreamInvalidAccessError } from '@/modules/core/errors/stream' import { CreateCommentInput, @@ -42,9 +41,10 @@ import { UpdateComment, ValidateInputAttachments } from '@/modules/comments/domain/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' type AuthorizeProjectCommentsAccessDeps = { - getStream: typeof getStream + getStream: GetStream adminOverrideEnabled: typeof adminOverrideEnabled } @@ -289,7 +289,7 @@ export const editCommentAndNotifyFactory = export const archiveCommentAndNotifyFactory = (deps: { getComment: GetComment - getStream: typeof getStream + getStream: GetStream updateComment: UpdateComment addCommentArchivedActivity: typeof addCommentArchivedActivity }): ArchiveCommentAndNotify => diff --git a/packages/server/modules/comments/tests/comments.spec.js b/packages/server/modules/comments/tests/comments.spec.js index 7ab7f3920..9fc91e1c9 100644 --- a/packages/server/modules/comments/tests/comments.spec.js +++ b/packages/server/modules/comments/tests/comments.spec.js @@ -56,8 +56,8 @@ const { db } = require('@/db/knex') const { getBlobsFactory } = require('@/modules/blobstorage/repositories') const { CommentsEmitter } = require('@/modules/comments/events/emitter') const { - getStream, - markCommitStreamUpdated + markCommitStreamUpdated, + getStreamFactory } = require('@/modules/core/repositories/streams') const { createCommitByBranchIdFactory, @@ -79,6 +79,7 @@ const { } = require('@/modules/activitystream/services/commitActivity') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const getStream = getStreamFactory({ db }) const streamResourceCheck = streamResourceCheckFactory({ checkStreamResourceAccess: checkStreamResourceAccessFactory({ db }) }) diff --git a/packages/server/modules/core/domain/streams/operations.ts b/packages/server/modules/core/domain/streams/operations.ts new file mode 100644 index 000000000..4ab040bef --- /dev/null +++ b/packages/server/modules/core/domain/streams/operations.ts @@ -0,0 +1,21 @@ +import { StreamWithOptionalRole } from '@/modules/core/domain/streams/types' +import { Optional } from '@speckle/shared' +import { Knex } from 'knex' + +export type GetStreams = ( + streamIds: string[], + options?: Partial<{ + userId: string + trx: Knex.Transaction + }> +) => Promise + +export type GetStream = ( + params: { + streamId?: string + userId?: string + }, + options?: Partial<{ + trx: Knex.Transaction + }> +) => Promise> diff --git a/packages/server/modules/core/domain/streams/types.ts b/packages/server/modules/core/domain/streams/types.ts new file mode 100644 index 000000000..825b475da --- /dev/null +++ b/packages/server/modules/core/domain/streams/types.ts @@ -0,0 +1,12 @@ +import { StreamRecord } from '@/modules/core/helpers/types' +import { StreamRoles } from '@speckle/shared' + +export type Stream = StreamRecord +export type Project = Stream + +export type StreamWithOptionalRole = Stream & { + /** + * Available, if query joined this data StreamAcl + */ + role?: StreamRoles +} diff --git a/packages/server/modules/core/graph/directives/hasRole.ts b/packages/server/modules/core/graph/directives/hasRole.ts index 3aee28b99..ad411854d 100644 --- a/packages/server/modules/core/graph/directives/hasRole.ts +++ b/packages/server/modules/core/graph/directives/hasRole.ts @@ -18,8 +18,9 @@ import { getUserAclRoleFactory, getUserServerRoleFactory } from '@/modules/shared/repositories/acl' -import { getStream } from '@/modules/core/repositories/streams' +import { getStreamFactory } from '@/modules/core/repositories/streams' +const getStream = getStreamFactory({ db }) const throwForNotHavingServerRole = throwForNotHavingServerRoleFactory({ validateServerRole: validateServerRoleBuilderFactory({ getRoles: getRolesFactory({ db }) diff --git a/packages/server/modules/core/graph/resolvers/branches.js b/packages/server/modules/core/graph/resolvers/branches.js index d9edcddec..caccf8bfa 100644 --- a/packages/server/modules/core/graph/resolvers/branches.js +++ b/packages/server/modules/core/graph/resolvers/branches.js @@ -33,8 +33,8 @@ const { addBranchDeletedActivity } = require('@/modules/activitystream/services/branchActivity') const { - getStream, - markBranchStreamUpdated + markBranchStreamUpdated, + getStreamFactory } = require('@/modules/core/repositories/streams') const { ModelsEmitter } = require('@/modules/core/events/modelsEmitter') @@ -43,6 +43,7 @@ const BRANCH_CREATED = BranchPubsubEvents.BranchCreated const BRANCH_UPDATED = BranchPubsubEvents.BranchUpdated const BRANCH_DELETED = BranchPubsubEvents.BranchDeleted +const getStream = getStreamFactory({ db }) const getBranchById = getBranchByIdFactory({ db }) const getStreamBranchByName = getStreamBranchByNameFactory({ db }) const createBranchAndNotify = createBranchAndNotifyFactory({ diff --git a/packages/server/modules/core/graph/resolvers/commits.js b/packages/server/modules/core/graph/resolvers/commits.js index 3965bdf59..842b7b69f 100644 --- a/packages/server/modules/core/graph/resolvers/commits.js +++ b/packages/server/modules/core/graph/resolvers/commits.js @@ -57,9 +57,9 @@ const { const { db } = require('@/db/knex') const { markCommitStreamUpdated, - getStream, getCommitStream, - getStreams + getStreamFactory, + getStreamsFactory } = require('@/modules/core/repositories/streams') const { markCommitBranchUpdatedFactory, @@ -81,6 +81,8 @@ const COMMIT_CREATED = CommitPubsubEvents.CommitCreated const COMMIT_UPDATED = CommitPubsubEvents.CommitUpdated const COMMIT_DELETED = CommitPubsubEvents.CommitDeleted +const getStream = getStreamFactory({ db }) +const getStreams = getStreamsFactory({ db }) const deleteCommitAndNotify = deleteCommitAndNotifyFactory({ getCommit: getCommitFactory({ db }), markCommitStreamUpdated, diff --git a/packages/server/modules/core/graph/resolvers/models.ts b/packages/server/modules/core/graph/resolvers/models.ts index f65d0d07f..f17e9c903 100644 --- a/packages/server/modules/core/graph/resolvers/models.ts +++ b/packages/server/modules/core/graph/resolvers/models.ts @@ -52,9 +52,13 @@ import { addBranchDeletedActivity, addBranchUpdatedActivity } from '@/modules/activitystream/services/branchActivity' -import { getStream, markBranchStreamUpdated } from '@/modules/core/repositories/streams' +import { + getStreamFactory, + markBranchStreamUpdated +} from '@/modules/core/repositories/streams' import { ModelsEmitter } from '@/modules/core/events/modelsEmitter' +const getStream = getStreamFactory({ db }) const getStreamObjects = getStreamObjectsFactory({ db }) const getViewerResourceGroups = getViewerResourceGroupsFactory({ getStreamObjects, diff --git a/packages/server/modules/core/graph/resolvers/projects.ts b/packages/server/modules/core/graph/resolvers/projects.ts index 055aa21b2..364626335 100644 --- a/packages/server/modules/core/graph/resolvers/projects.ts +++ b/packages/server/modules/core/graph/resolvers/projects.ts @@ -1,3 +1,4 @@ +import { db } from '@/db/knex' import { RateLimitError } from '@/modules/core/errors/ratelimit' import { StreamNotFoundError } from '@/modules/core/errors/stream' import { WorkspacesModuleDisabledError } from '@/modules/core/errors/workspaces' @@ -13,7 +14,7 @@ import { getUserStreamsCount, getUserStreams, getStreamCollaborators, - getStream + getStreamFactory } from '@/modules/core/repositories/streams' import { getRateLimitResult, @@ -36,6 +37,8 @@ import { } from '@/modules/shared/utils/subscriptions' import { has } from 'lodash' +const getStream = getStreamFactory({ db }) + export = { Query: { async project(_parent, args, context) { diff --git a/packages/server/modules/core/graph/resolvers/streams.ts b/packages/server/modules/core/graph/resolvers/streams.ts index 1a29f72d9..e371993fa 100644 --- a/packages/server/modules/core/graph/resolvers/streams.ts +++ b/packages/server/modules/core/graph/resolvers/streams.ts @@ -1,5 +1,4 @@ import { - getStream, getStreams, getStreamUsers, favoriteStream, @@ -24,7 +23,8 @@ import { getDiscoverableStreams } from '@/modules/core/services/streams/discover import { get } from 'lodash' import { getUserStreamsCount, - getUserStreams + getUserStreams, + getStreamFactory } from '@/modules/core/repositories/streams' import { deleteStreamAndNotify, @@ -50,6 +50,8 @@ import { getInvitationTargetUsersFactory } from '@/modules/serverinvites/service import { getUsers } from '@/modules/core/repositories/users' import { BadRequestError } from '@/modules/shared/errors' +const getStream = getStreamFactory({ db }) + const getUserStreamsCore = async ( forOtherUser: boolean, parent: { id: string }, diff --git a/packages/server/modules/core/graph/resolvers/versions.ts b/packages/server/modules/core/graph/resolvers/versions.ts index 185e621f3..4c9a94cad 100644 --- a/packages/server/modules/core/graph/resolvers/versions.ts +++ b/packages/server/modules/core/graph/resolvers/versions.ts @@ -41,8 +41,8 @@ import { } from '@/modules/core/repositories/branches' import { getCommitStream, - getStream, - getStreams, + getStreamFactory, + getStreamsFactory, markCommitStreamUpdated } from '@/modules/core/repositories/streams' import { VersionsEmitter } from '@/modules/core/events/versionsEmitter' @@ -53,6 +53,8 @@ import { } from '@/modules/activitystream/services/commitActivity' import { getObjectFactory } from '@/modules/core/repositories/objects' +const getStream = getStreamFactory({ db }) +const getStreams = getStreamsFactory({ db }) const getObject = getObjectFactory({ db }) const createCommitByBranchId = createCommitByBranchIdFactory({ createCommit: createCommitFactory({ db }), diff --git a/packages/server/modules/core/loaders.ts b/packages/server/modules/core/loaders.ts index 387a31ac5..9ff4e97eb 100644 --- a/packages/server/modules/core/loaders.ts +++ b/packages/server/modules/core/loaders.ts @@ -3,12 +3,12 @@ import { getBatchUserFavoriteData, getBatchStreamFavoritesCounts, getOwnedFavoritesCountByUserIds, - getStreams, getStreamRoles, getStreamsSourceApps, getCommitStreams, StreamWithCommitId, - getUserStreamCounts + getUserStreamCounts, + getStreamsFactory } from '@/modules/core/repositories/streams' import { UserWithOptionalRole, getUsers } from '@/modules/core/repositories/users' import { keyBy } from 'lodash' @@ -88,6 +88,7 @@ import { getAppScopesFactory } from '@/modules/auth/repositories' const simpleTupleCacheKey = (key: [string, string]) => `${key[0]}:${key[1]}` +const getStreams = getStreamsFactory({ db }) const getStreamPendingModels = getStreamPendingModelsFactory({ db }) const getAppScopes = getAppScopesFactory({ db }) const getAutomations = getAutomationsFactory({ db }) diff --git a/packages/server/modules/core/repositories/streams.ts b/packages/server/modules/core/repositories/streams.ts index fa86a50ff..bd1f1aaf0 100644 --- a/packages/server/modules/core/repositories/streams.ts +++ b/packages/server/modules/core/repositories/streams.ts @@ -57,7 +57,7 @@ import { } from '@/modules/core/errors/stream' import { metaHelpers } from '@/modules/core/helpers/meta' import { removePrivateFields } from '@/modules/core/helpers/userHelper' -import { db as defaultKnexInstance } from '@/db/knex' +import { db, db as defaultKnexInstance } from '@/db/knex' import { DeleteProjectRole, GetProject, @@ -66,17 +66,13 @@ import { GetRolesByUserId, UpsertProjectRole } from '@/modules/core/domain/projects/operations' +import { StreamWithOptionalRole } from '@/modules/core/domain/streams/types' +import { GetStream, GetStreams } from '@/modules/core/domain/streams/operations' +export type { StreamWithOptionalRole } const tables = { - streams: (db: Knex) => db('streams'), - streamAcl: (db: Knex) => db('stream_acl') -} - -export type StreamWithOptionalRole = StreamRecord & { - /** - * Available, if query joined this data StreamAcl - */ - role?: StreamRoles + streams: (db: Knex) => db(Streams.name), + streamAcl: (db: Knex) => db(StreamAcl.name) } /** @@ -130,56 +126,62 @@ const generateStreamName = () => { /** * Get multiple streams. If userId is specified, the role will be resolved as well. */ -export async function getStreams( - streamIds: string[], - options: Partial<{ userId: string; trx: Knex.Transaction }> = {} -) { - const { userId, trx } = options - if (!streamIds?.length) throw new InvalidArgumentError('Empty stream IDs') +export const getStreamsFactory = + (deps: { db: Knex }): GetStreams => + async ( + streamIds: string[], + options: Partial<{ userId: string; trx: Knex.Transaction }> = {} + ) => { + const { userId, trx } = options + if (!streamIds?.length) throw new InvalidArgumentError('Empty stream IDs') - const q = Streams.knex().whereIn(Streams.col.id, streamIds) + const q = tables.streams(deps.db).whereIn(Streams.col.id, streamIds) - if (userId) { - q.select([ - ...Object.values(Streams.col), - // Getting first role from grouped results - knex.raw(`(array_agg("stream_acl"."role"))[1] as role`) - ]) - q.leftJoin(StreamAcl.name, function () { - this.on(StreamAcl.col.resourceId, Streams.col.id).andOnVal( - StreamAcl.col.userId, - userId - ) - }) - q.groupBy(Streams.col.id) + if (userId) { + q.select([ + ...Object.values(Streams.col), + // Getting first role from grouped results + knex.raw(`(array_agg("stream_acl"."role"))[1] as role`) + ]) + q.leftJoin(StreamAcl.name, function () { + this.on(StreamAcl.col.resourceId, Streams.col.id).andOnVal( + StreamAcl.col.userId, + userId + ) + }) + q.groupBy(Streams.col.id) + } + + if (trx) { + q.transacting(trx) + } + + return await q } - if (trx) { - q.transacting(trx) - } - - return await q -} - /** * Get a single stream. If userId is specified, the role will be resolved as well. */ -export async function getStream( - params: { streamId?: string; userId?: string }, - options?: Partial<{ trx: Knex.Transaction }> -): Promise> { - const { streamId, userId } = params - if (!streamId) throw new InvalidArgumentError('Invalid stream ID') +export const getStreamFactory = + (deps: { db: Knex }): GetStream => + async ( + params: { streamId?: string; userId?: string }, + options?: Partial<{ trx: Knex.Transaction }> + ): Promise> => { + const { streamId, userId } = params + if (!streamId) throw new InvalidArgumentError('Invalid stream ID') - const streams = await getStreams([streamId], { userId, ...(options || {}) }) - return >streams[0] -} + const streams = await getStreamsFactory(deps)([streamId], { + userId, + ...(options || {}) + }) + return >streams[0] + } -// TODO: Inject db export const getProjectFactory = - (): GetProject => + (deps: { db: Knex }): GetProject => async ({ projectId }) => { - const project = await getStream({ streamId: projectId }) + const project = await getStreamFactory(deps)({ streamId: projectId }) if (!project) { throw new StreamNotFoundError() @@ -1181,7 +1183,7 @@ export async function revokeStreamPermissions( * Mark stream as the onboarding base stream from which user onboarding streams will be cloned */ export async function markOnboardingBaseStream(streamId: string, version: string) { - const stream = await getStream({ streamId }) + const stream = await getStreamFactory({ db })({ streamId }) if (!stream) { throw new Error(`Stream ${streamId} not found`) } diff --git a/packages/server/modules/core/rest/authUtils.js b/packages/server/modules/core/rest/authUtils.js index 623d07011..18573851e 100644 --- a/packages/server/modules/core/rest/authUtils.js +++ b/packages/server/modules/core/rest/authUtils.js @@ -1,13 +1,15 @@ 'use strict' const { validateScopes, authorizeResolver } = require('@/modules/shared') -const { getStream } = require('../services/streams') const { Roles, Scopes } = require('@speckle/shared') const { throwForNotHavingServerRole } = require('@/modules/shared/authz') const { DatabaseError } = require('@/modules/shared/errors') +const { getStreamFactory } = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') module.exports = { async validatePermissionsReadStream(streamId, req) { + const getStream = getStreamFactory({ db }) const stream = await getStream({ streamId, userId: req.context.userId }) if (stream?.isPublic) return { result: true, status: 200 } diff --git a/packages/server/modules/core/services/branch/management.ts b/packages/server/modules/core/services/branch/management.ts index 0198418b4..033e7bcae 100644 --- a/packages/server/modules/core/services/branch/management.ts +++ b/packages/server/modules/core/services/branch/management.ts @@ -19,7 +19,7 @@ import { } from '@/modules/core/graph/generated/graphql' import { BranchRecord } from '@/modules/core/helpers/types' -import { getStream, markBranchStreamUpdated } from '@/modules/core/repositories/streams' +import { markBranchStreamUpdated } from '@/modules/core/repositories/streams' import { has } from 'lodash' import { isBranchDeleteInput, isBranchUpdateInput } from '@/modules/core/helpers/branch' import { ModelsEmitter, ModelsEventsEmitter } from '@/modules/core/events/modelsEmitter' @@ -33,6 +33,7 @@ import { UpdateBranch, UpdateBranchAndNotify } from '@/modules/core/domain/branches/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' const isBranchCreateInput = ( i: BranchCreateInput | CreateModelInput @@ -109,7 +110,7 @@ export const updateBranchAndNotifyFactory = export const deleteBranchAndNotifyFactory = (deps: { - getStream: typeof getStream + getStream: GetStream getBranchById: GetBranchById modelsEventsEmitter: ModelsEventsEmitter markBranchStreamUpdated: typeof markBranchStreamUpdated diff --git a/packages/server/modules/core/services/commit/batchCommitActions.ts b/packages/server/modules/core/services/commit/batchCommitActions.ts index 83bac46f4..94b4cbf9e 100644 --- a/packages/server/modules/core/services/commit/batchCommitActions.ts +++ b/packages/server/modules/core/services/commit/batchCommitActions.ts @@ -12,6 +12,7 @@ import { MoveCommitsToBranch, ValidateAndBatchMoveCommits } from '@/modules/core/domain/commits/operations' +import { GetStreams } from '@/modules/core/domain/streams/operations' import { CommitInvalidAccessError, CommitBatchUpdateError @@ -27,7 +28,7 @@ import { deleteCommitsFactory, getCommitsFactory } from '@/modules/core/repositories/commits' -import { getStreams } from '@/modules/core/repositories/streams' +import { getStreamsFactory } from '@/modules/core/repositories/streams' import { ensureError } from '@/modules/shared/helpers/errorHelper' import { difference, groupBy, has, keyBy } from 'lodash' @@ -38,7 +39,7 @@ const isOldBatchInput = (i: CommitBatchInput): i is OldBatchInput => has(i, 'com type ValidateBatchBaseRulesDeps = { getCommits: GetCommits - getStreams: typeof getStreams + getStreams: GetStreams } /** @@ -147,7 +148,7 @@ async function validateCommitsDelete( ) { const validateBatchBaseRules = validateBatchBaseRulesFactory({ getCommits: getCommitsFactory({ db }), - getStreams + getStreams: getStreamsFactory({ db }) }) return await validateBatchBaseRules(params, userId) } diff --git a/packages/server/modules/core/services/commit/management.ts b/packages/server/modules/core/services/commit/management.ts index fc19f949f..1f41ee9a3 100644 --- a/packages/server/modules/core/services/commit/management.ts +++ b/packages/server/modules/core/services/commit/management.ts @@ -25,6 +25,7 @@ import { UpdateCommitAndNotify } from '@/modules/core/domain/commits/operations' import { GetObject } from '@/modules/core/domain/objects/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' import { CommitCreateError, CommitDeleteError, @@ -45,7 +46,6 @@ import { CommitRecord } from '@/modules/core/helpers/types' import { getCommitFactory } from '@/modules/core/repositories/commits' import { getCommitStream, - getStream, markCommitStreamUpdated } from '@/modules/core/repositories/streams' import { ensureError, MaybeNullOrUndefined, Nullable, Roles } from '@speckle/shared' @@ -253,7 +253,7 @@ const isOldVersionUpdateInput = ( export const updateCommitAndNotifyFactory = (deps: { getCommit: GetCommit - getStream: typeof getStream + getStream: GetStream getCommitStream: typeof getCommitStream getStreamBranchByName: GetStreamBranchByName getCommitBranch: GetCommitBranch diff --git a/packages/server/modules/core/services/streams.js b/packages/server/modules/core/services/streams.js index 77404e3a6..3e9a98681 100644 --- a/packages/server/modules/core/services/streams.js +++ b/packages/server/modules/core/services/streams.js @@ -2,7 +2,6 @@ const _ = require('lodash') const { Streams, StreamAcl, knex } = require('@/modules/core/dbSchema') const { - getStream, getFavoritedStreams, getFavoritedStreamsCount, setStreamFavorited, @@ -10,7 +9,8 @@ const { deleteStream: deleteStreamFromDb, updateStream: updateStreamInDb, revokeStreamPermissions, - grantStreamPermissions + grantStreamPermissions, + getStreamFactory } = require('@/modules/core/repositories/streams') const { UnauthorizedError, InvalidArgumentError } = require('@/modules/shared/errors') const { dbLogger } = require('@/logging/logging') @@ -42,8 +42,6 @@ module.exports = { return id }, - getStream, - /** * @deprecated Use updateStreamAndNotify or use the repository function directly * @param {import('@/modules/core/graph/generated/graphql').StreamUpdateInput} update @@ -205,6 +203,8 @@ module.exports = { // Favorite/unfavorite the stream await setStreamFavorited({ streamId, userId, favorited }) + const getStream = getStreamFactory({ db: knex }) + // Get updated stream info return await getStream({ streamId, userId }) }, diff --git a/packages/server/modules/core/services/streams/clone.ts b/packages/server/modules/core/services/streams/clone.ts index a7fbbbc60..a5ca3a4cc 100644 --- a/packages/server/modules/core/services/streams/clone.ts +++ b/packages/server/modules/core/services/streams/clone.ts @@ -6,7 +6,7 @@ import { } from '@/modules/core/helpers/types' import { createStream, - getStream, + getStreamFactory, StreamWithOptionalRole } from '@/modules/core/repositories/streams' import { getUser, UserWithOptionalRole } from '@/modules/core/repositories/users' @@ -82,6 +82,7 @@ const prepareState = async ( userId: string, sourceStreamId: string ): Promise => { + const getStream = getStreamFactory({ db }) const targetStream = await getStream({ streamId: sourceStreamId }) if (!targetStream) { throw new StreamCloneError('Clonable source stream not found', { diff --git a/packages/server/modules/core/services/streams/management.ts b/packages/server/modules/core/services/streams/management.ts index bef1de143..f8889b0dd 100644 --- a/packages/server/modules/core/services/streams/management.ts +++ b/packages/server/modules/core/services/streams/management.ts @@ -17,7 +17,7 @@ import { StreamRecord } from '@/modules/core/helpers/types' import { createStream, deleteStream, - getStream, + getStreamFactory, updateStream } from '@/modules/core/repositories/streams' import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' @@ -92,6 +92,7 @@ export async function createStreamReturnRecord( // Invite contributors? if (!isProjectCreateInput(params) && params.withContributors?.length) { // TODO: should be injected in the resolver + const getStream = getStreamFactory({ db }) await inviteUsersToProjectFactory({ createAndSendInvite: createAndSendInviteFactory({ findUserByTarget: findUserByTargetFactory(), @@ -188,6 +189,7 @@ export async function updateStreamAndNotify( updaterResourceAccessRules ) + const getStream = getStreamFactory({ db }) const oldStream = await getStream({ streamId: update.id, userId: updaterId }) if (!oldStream) { throw new StreamUpdateError('Stream not found', { diff --git a/packages/server/modules/core/services/streams/streamAccessService.js b/packages/server/modules/core/services/streams/streamAccessService.js index 88525adc8..14955ffa8 100644 --- a/packages/server/modules/core/services/streams/streamAccessService.js +++ b/packages/server/modules/core/services/streams/streamAccessService.js @@ -16,9 +16,9 @@ const { addStreamPermissionsAddedActivityFactory } = require('@/modules/activitystream/services/streamActivity') const { - getStream, revokeStreamPermissions, - grantStreamPermissions + grantStreamPermissions, + getStreamFactory } = require('@/modules/core/repositories/streams') const { ServerAcl } = require('@/modules/core/dbSchema') @@ -34,6 +34,7 @@ const { db } = require('@/db/knex') * @returns */ async function isStreamCollaborator(userId, streamId) { + const getStream = getStreamFactory({ db }) const stream = await getStream({ streamId, userId }) return !!stream.role } diff --git a/packages/server/modules/core/tests/branches.spec.js b/packages/server/modules/core/tests/branches.spec.js index 79a7df7fc..c68bbe0c9 100644 --- a/packages/server/modules/core/tests/branches.spec.js +++ b/packages/server/modules/core/tests/branches.spec.js @@ -30,9 +30,9 @@ const { addBranchDeletedActivity } = require('@/modules/activitystream/services/branchActivity') const { - getStream, markBranchStreamUpdated, - markCommitStreamUpdated + markCommitStreamUpdated, + getStreamFactory } = require('@/modules/core/repositories/streams') const { ModelsEmitter } = require('@/modules/core/events/modelsEmitter') const { @@ -52,6 +52,7 @@ const { getObjectFactory } = require('@/modules/core/repositories/objects') const db = knex const Commits = () => knex('commits') +const getStream = getStreamFactory({ db: knex }) const getBranchById = getBranchByIdFactory({ db: knex }) const getStreamBranchByName = getStreamBranchByNameFactory({ db: knex }) const createBranch = createBranchFactory({ db: knex }) diff --git a/packages/server/modules/core/tests/commits.spec.js b/packages/server/modules/core/tests/commits.spec.js index 90f6c7de9..238caa564 100644 --- a/packages/server/modules/core/tests/commits.spec.js +++ b/packages/server/modules/core/tests/commits.spec.js @@ -47,7 +47,7 @@ const { const { markCommitStreamUpdated, getCommitStream, - getStream + getStreamFactory } = require('@/modules/core/repositories/streams') const { addCommitDeletedActivity, @@ -57,6 +57,7 @@ const { const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const getStream = getStreamFactory({ db }) const createBranch = createBranchFactory({ db }) const createBranchAndNotify = createBranchAndNotifyFactory({ createBranch, diff --git a/packages/server/modules/core/tests/discoverableStreams.spec.ts b/packages/server/modules/core/tests/discoverableStreams.spec.ts index 21dfcd4d1..31ecee2fa 100644 --- a/packages/server/modules/core/tests/discoverableStreams.spec.ts +++ b/packages/server/modules/core/tests/discoverableStreams.spec.ts @@ -1,6 +1,10 @@ import { buildApolloServer } from '@/app' +import { db } from '@/db/knex' import { Streams, Users } from '@/modules/core/dbSchema' -import { getStream, setStreamFavorited } from '@/modules/core/repositories/streams' +import { + getStreamFactory, + setStreamFavorited +} from '@/modules/core/repositories/streams' import { Nullable, Optional } from '@/modules/shared/helpers/typeHelper' import { BasicTestUser, createTestUsers } from '@/test/authHelper' import { @@ -27,6 +31,7 @@ import { shuffle } from 'lodash' const READABLE_DISCOVERABLE_STREAM_COUNT = 15 const cleanup = async () => await truncateTables([Streams.name, Users.name]) +const getStream = getStreamFactory({ db }) describe('Discoverable streams', () => { let apollo: ServerAndContext diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index 6634eadb4..f4a7e89f0 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -1,7 +1,6 @@ import { expect } from 'chai' import { createStream, - getStream, updateStream, deleteStream, getStreamUsers, @@ -29,6 +28,7 @@ import { } from '@/test/speckle-helpers/streamHelper' import { StreamWithOptionalRole, + getStreamFactory, markBranchStreamUpdated, markCommitStreamUpdated, revokeStreamPermissions @@ -74,6 +74,7 @@ import { VersionsEmitter } from '@/modules/core/events/versionsEmitter' import { addCommitCreatedActivity } from '@/modules/activitystream/services/commitActivity' import { getObjectFactory } from '@/modules/core/repositories/objects' +const getStream = getStreamFactory({ db }) const getStreamBranchByName = getStreamBranchByNameFactory({ db }) const createBranch = createBranchFactory({ db }) const deleteBranchAndNotify = deleteBranchAndNotifyFactory({ diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index df8efee0c..98ee10d2d 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -21,11 +21,7 @@ const { validateToken, getUserTokens } = require('../services/tokens') -const { - grantPermissionsStream, - createStream, - getStream -} = require('../services/streams') +const { grantPermissionsStream, createStream } = require('../services/streams') const { getBranchesByStreamId } = require('../services/branches') @@ -52,13 +48,17 @@ const { createCommitByBranchIdFactory, createCommitByBranchNameFactory } = require('@/modules/core/services/commit/management') -const { markCommitStreamUpdated } = require('@/modules/core/repositories/streams') +const { + markCommitStreamUpdated, + getStreamFactory +} = require('@/modules/core/repositories/streams') const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { addCommitCreatedActivity } = require('@/modules/activitystream/services/commitActivity') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const getStream = getStreamFactory({ db }) const createBranch = createBranchFactory({ db }) const getCommit = getCommitFactory({ db }) diff --git a/packages/server/modules/cross-server-sync/index.ts b/packages/server/modules/cross-server-sync/index.ts index 645c45e82..925d679e7 100644 --- a/packages/server/modules/cross-server-sync/index.ts +++ b/packages/server/modules/cross-server-sync/index.ts @@ -42,8 +42,8 @@ import { } from '@/modules/core/repositories/objects' import { getOnboardingBaseStream, - getStream, getStreamCollaborators, + getStreamFactory, markCommitStreamUpdated, markOnboardingBaseStream } from '@/modules/core/repositories/streams' @@ -68,6 +68,7 @@ const crossServerSyncModule: SpeckleModule = { finalize() { crossServerSyncLogger.info('⬇️ Ensuring base onboarding stream asynchronously...') + const getStream = getStreamFactory({ db }) const getObject = getObjectFactory({ db }) const getStreamObjects = getStreamObjectsFactory({ db }) const markCommentViewed = markCommentViewedFactory({ db }) diff --git a/packages/server/modules/cross-server-sync/services/commit.ts b/packages/server/modules/cross-server-sync/services/commit.ts index 671616b54..12f838689 100644 --- a/packages/server/modules/cross-server-sync/services/commit.ts +++ b/packages/server/modules/cross-server-sync/services/commit.ts @@ -2,7 +2,7 @@ import fetch from 'cross-fetch' import { ApolloClient, NormalizedCacheObject, gql } from '@apollo/client/core' import { getFrontendOrigin } from '@/modules/shared/helpers/envHelper' import { CreateCommentInput } from '@/test/graphql/generated/graphql' -import { getStream, getStreamCollaborators } from '@/modules/core/repositories/streams' +import { getStreamCollaborators } from '@/modules/core/repositories/streams' import { Roles, timeoutAt } from '@speckle/shared' import { createObject } from '@/modules/core/services/objects' import ObjectLoader from '@speckle/objectloader' @@ -31,6 +31,7 @@ import { import { GetStreamBranchByName } from '@/modules/core/domain/branches/operations' import { CreateCommitByBranchId } from '@/modules/core/domain/commits/operations' import { GetObject } from '@/modules/core/domain/objects/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' type LocalResources = Awaited>> type LocalResourcesWithCommit = LocalResources & { newCommitId: string } @@ -218,7 +219,7 @@ const parseIncomingUrl = async (url: string, token?: string) => { } type GetLocalResourcesDeps = { - getStream: typeof getStream + getStream: GetStream getStreamBranchByName: GetStreamBranchByName getStreamCollaborators: typeof getStreamCollaborators getUser: typeof getUser diff --git a/packages/server/modules/fileuploads/index.ts b/packages/server/modules/fileuploads/index.ts index 8009edc93..4600c73f6 100644 --- a/packages/server/modules/fileuploads/index.ts +++ b/packages/server/modules/fileuploads/index.ts @@ -12,16 +12,17 @@ import { db } from '@/db/knex' import { publish } from '@/modules/shared/utils/subscriptions' import { SpeckleModule } from '@/modules/shared/helpers/typeHelper' import { streamWritePermissionsPipelineFactory } from '@/modules/shared/authz' -import { getStream } from '@/modules/core/repositories/streams' import { getRolesFactory } from '@/modules/shared/repositories/roles' import { getAutomationProjectFactory } from '@/modules/automate/repositories/automations' import { getStreamBranchByNameFactory } from '@/modules/core/repositories/branches' +import { getStreamFactory } from '@/modules/core/repositories/streams' const insertNewUploadAndNotify = insertNewUploadAndNotifyFactory({ getStreamBranchByName: getStreamBranchByNameFactory({ db }), saveUploadFile: saveUploadFileFactory({ db }), publish }) +const getStream = getStreamFactory({ db }) const saveFileUploads = async ({ userId, diff --git a/packages/server/modules/notifications/services/handlers/activityDigest.ts b/packages/server/modules/notifications/services/handlers/activityDigest.ts index 66c4c0128..459f62a61 100644 --- a/packages/server/modules/notifications/services/handlers/activityDigest.ts +++ b/packages/server/modules/notifications/services/handlers/activityDigest.ts @@ -30,8 +30,8 @@ import { StreamActivitySummary } from '@/modules/activitystream/domain/types' import { createActivitySummaryFactory } from '@/modules/activitystream/services/summary' -import { getStream } from '@/modules/core/services/streams' import { getActivityFactory } from '@/modules/activitystream/repositories' +import { getStreamFactory } from '@/modules/core/repositories/streams' const digestNotificationEmailHandlerFactory = ( @@ -436,7 +436,7 @@ const digestNotificationEmailHandler = digestNotificationEmailHandlerFactory({ }) }), createActivitySummary: createActivitySummaryFactory({ - getStream, + getStream: getStreamFactory({ db }), getActivity: getActivityFactory({ db }) }), getServerInfo, diff --git a/packages/server/modules/notifications/services/handlers/mentionedInComment.ts b/packages/server/modules/notifications/services/handlers/mentionedInComment.ts index bf81ee695..77bcad522 100644 --- a/packages/server/modules/notifications/services/handlers/mentionedInComment.ts +++ b/packages/server/modules/notifications/services/handlers/mentionedInComment.ts @@ -2,10 +2,12 @@ import { db } from '@/db/knex' import { GetComment } from '@/modules/comments/domain/operations' import { ExtendedComment } from '@/modules/comments/domain/types' import { getCommentFactory } from '@/modules/comments/repositories/comments' +import { GetStream } from '@/modules/core/domain/streams/operations' +import { StreamWithOptionalRole } from '@/modules/core/domain/streams/types' import { Roles } from '@/modules/core/helpers/mainConstants' import { getCommentRoute } from '@/modules/core/helpers/routeHelper' import { ServerInfo } from '@/modules/core/helpers/types' -import { getStream, StreamWithOptionalRole } from '@/modules/core/repositories/streams' +import { getStreamFactory } from '@/modules/core/repositories/streams' import { getUser, UserWithOptionalRole } from '@/modules/core/repositories/users' import { getServerInfo } from '@/modules/core/services/generic' import { @@ -163,7 +165,7 @@ function buildEmailTemplateParams( const mentionedInCommentHandlerFactory = (deps: { getUser: typeof getUser - getStream: typeof getStream + getStream: GetStream getComment: GetComment getServerInfo: typeof getServerInfo renderEmail: typeof renderEmail @@ -220,7 +222,7 @@ const mentionedInCommentHandlerFactory = const handler: NotificationHandler = async (...args) => { const mentionedInCommentHandler = mentionedInCommentHandlerFactory({ getUser, - getStream, + getStream: getStreamFactory({ db }), getComment: getCommentFactory({ db }), getServerInfo, renderEmail, diff --git a/packages/server/modules/notifications/services/handlers/newStreamAccessRequest.ts b/packages/server/modules/notifications/services/handlers/newStreamAccessRequest.ts index 9daba03ed..e9dccc5bd 100644 --- a/packages/server/modules/notifications/services/handlers/newStreamAccessRequest.ts +++ b/packages/server/modules/notifications/services/handlers/newStreamAccessRequest.ts @@ -8,7 +8,6 @@ import { NotificationHandler } from '@/modules/notifications/helpers/types' import { NotificationValidationError } from '@/modules/notifications/errors' -import { getStream } from '@/modules/core/repositories/streams' import { Roles } from '@/modules/core/helpers/mainConstants' import { buildAbsoluteFrontendUrlFromPath, @@ -22,11 +21,13 @@ import { import { getServerInfo } from '@/modules/core/services/generic' import { db } from '@/db/knex' import { GetPendingAccessRequest } from '@/modules/accessrequests/domain/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' +import { getStreamFactory } from '@/modules/core/repositories/streams' type ValidateMessageDeps = { getPendingAccessRequest: GetPendingAccessRequest getUser: typeof getUser - getStream: typeof getStream + getStream: GetStream } const validateMessageFactory = @@ -150,7 +151,7 @@ const handler: NotificationHandler = (...args) => renderEmail, sendEmail, getUser, - getStream, + getStream: getStreamFactory({ db }), getPendingAccessRequest: getPendingAccessRequestFactory({ db }) }) return newStreamAccessRequestHandler(...args) diff --git a/packages/server/modules/notifications/services/handlers/streamAccessRequestApproved.ts b/packages/server/modules/notifications/services/handlers/streamAccessRequestApproved.ts index e8e1ae5a4..79b7ac43c 100644 --- a/packages/server/modules/notifications/services/handlers/streamAccessRequestApproved.ts +++ b/packages/server/modules/notifications/services/handlers/streamAccessRequestApproved.ts @@ -1,8 +1,10 @@ +import { db } from '@/db/knex' +import { GetStream } from '@/modules/core/domain/streams/operations' import { buildAbsoluteFrontendUrlFromPath, getStreamRoute } from '@/modules/core/helpers/routeHelper' -import { getStream } from '@/modules/core/repositories/streams' +import { getStreamFactory } from '@/modules/core/repositories/streams' import { getUser } from '@/modules/core/repositories/users' import { getServerInfo } from '@/modules/core/services/generic' import { @@ -18,7 +20,7 @@ import { type ValidateMessageDeps = { getUser: typeof getUser - getStream: typeof getStream + getStream: GetStream } const validateMessageFactory = @@ -129,7 +131,7 @@ const handler: NotificationHandler = async ( renderEmail, sendEmail, getUser, - getStream + getStream: getStreamFactory({ db }) }) return streamAccessRequestApprovedHandler(...args) } diff --git a/packages/server/modules/previews/index.ts b/packages/server/modules/previews/index.ts index 8a2fc451c..78b06476a 100644 --- a/packages/server/modules/previews/index.ts +++ b/packages/server/modules/previews/index.ts @@ -18,7 +18,6 @@ import { checkStreamPermissionsFactory } from '@/modules/previews/services/management' import { getObject } from '@/modules/core/services/objects' -import { getStream } from '@/modules/core/repositories/streams' import { getObjectPreviewInfoFactory, createObjectPreviewFactory, @@ -30,6 +29,7 @@ import { getObjectCommitsWithStreamIdsFactory } from '@/modules/core/repositories/commits' import { SpeckleModule } from '@/modules/shared/helpers/typeHelper' +import { getStreamFactory } from '@/modules/core/repositories/streams' const httpErrorImage = (httpErrorCode: number) => require.resolve(`#/assets/previews/images/preview_${httpErrorCode}.png`) @@ -43,6 +43,7 @@ export const init: SpeckleModule['init'] = (app, isInitial) => { moduleLogger.info('📸 Init object preview module') } + const getStream = getStreamFactory({ db }) const getObjectPreviewBufferOrFilepath = getObjectPreviewBufferOrFilepathFactory({ getObject, getObjectPreviewInfo: getObjectPreviewInfoFactory({ db }), @@ -56,7 +57,8 @@ export const init: SpeckleModule['init'] = (app, isInitial) => { }) const checkStreamPermissions = checkStreamPermissionsFactory({ validateScopes, - authorizeResolver + authorizeResolver, + getStream }) app.options('/preview/:streamId/:angle?', cors()) diff --git a/packages/server/modules/previews/services/management.ts b/packages/server/modules/previews/services/management.ts index 40b0e8fe3..e4560c8e1 100644 --- a/packages/server/modules/previews/services/management.ts +++ b/packages/server/modules/previews/services/management.ts @@ -1,5 +1,5 @@ import { logger } from '@/logging/logging' -import { getStream } from '@/modules/core/repositories/streams' +import { GetStream } from '@/modules/core/domain/streams/operations' import { getObject } from '@/modules/core/services/objects' import { CheckStreamPermissions, @@ -83,7 +83,7 @@ export const getObjectPreviewBufferOrFilepathFactory = export const sendObjectPreviewFactory = (deps: { getObjectPreviewBufferOrFilepath: GetObjectPreviewBufferOrFilepath - getStream: typeof getStream + getStream: GetStream makeOgImage: typeof makeOgImage }): SendObjectPreview => async (req, res, streamId, objectId, angle) => { @@ -136,9 +136,10 @@ export const checkStreamPermissionsFactory = (deps: { validateScopes: typeof validateScopes authorizeResolver: typeof authorizeResolver + getStream: GetStream }): CheckStreamPermissions => async (req) => { - const stream = await getStream({ + const stream = await deps.getStream({ streamId: req.params.streamId, userId: req.context.userId }) diff --git a/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts b/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts index 7955cb6c7..683617ab3 100644 --- a/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts +++ b/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts @@ -43,7 +43,6 @@ import { } from '@/modules/serverinvites/services/projectInviteManagement' import { getUser, getUsers } from '@/modules/core/repositories/users' import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' -import { getStream } from '@/modules/core/repositories/streams' import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' import { getEventBus } from '@/modules/shared/services/eventBus' import { @@ -73,7 +72,9 @@ import { renderEmail } from '@/modules/emails/services/emailRendering' import { sendEmail } from '@/modules/emails/services/sending' import { publish } from '@/modules/shared/utils/subscriptions' import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { getStreamFactory } from '@/modules/core/repositories/streams' +const getStream = getStreamFactory({ db }) const requestNewEmailVerification = requestNewEmailVerificationFactory({ findEmail: findEmailFactory({ db }), getUser, diff --git a/packages/server/modules/serverinvites/services/coreEmailContents.ts b/packages/server/modules/serverinvites/services/coreEmailContents.ts index 181e0e39d..c33a0beea 100644 --- a/packages/server/modules/serverinvites/services/coreEmailContents.ts +++ b/packages/server/modules/serverinvites/services/coreEmailContents.ts @@ -1,9 +1,9 @@ +import { GetStream } from '@/modules/core/domain/streams/operations' import { getRegistrationRoute, getStreamRoute } from '@/modules/core/helpers/routeHelper' import { StreamRecord } from '@/modules/core/helpers/types' -import { getStream } from '@/modules/core/repositories/streams' import { EmailTemplateParams, sanitizeMessage @@ -162,7 +162,7 @@ const buildProjectEmailTemplateParams = ( } } -type BuildProjectInviteContentsFactoryDeps = { getStream: typeof getStream } +type BuildProjectInviteContentsFactoryDeps = { getStream: GetStream } const buildProjectInviteContentsFactory = (deps: BuildProjectInviteContentsFactoryDeps): BuildProjectInviteEmailContents => diff --git a/packages/server/modules/serverinvites/services/coreFinalization.ts b/packages/server/modules/serverinvites/services/coreFinalization.ts index 242142d37..67f2c7dd9 100644 --- a/packages/server/modules/serverinvites/services/coreFinalization.ts +++ b/packages/server/modules/serverinvites/services/coreFinalization.ts @@ -1,7 +1,7 @@ import { AddStreamInviteDeclinedActivity } from '@/modules/activitystream/domain/operations' +import { GetStream } from '@/modules/core/domain/streams/operations' import { StreamInvalidAccessError } from '@/modules/core/errors/stream' import { isResourceAllowed } from '@/modules/core/helpers/token' -import { getStream } from '@/modules/core/repositories/streams' import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/streamAccessService' import { ProjectInviteResourceType } from '@/modules/serverinvites/domain/constants' import { InviteFinalizingError } from '@/modules/serverinvites/errors' @@ -13,7 +13,7 @@ import { import { Roles } from '@speckle/shared' type ValidateProjectInviteBeforeFinalizationFactoryDeps = { - getProject: typeof getStream + getProject: GetStream } export const validateProjectInviteBeforeFinalizationFactory = @@ -74,7 +74,7 @@ export const validateProjectInviteBeforeFinalizationFactory = } type ProcessFinalizedProjectInviteFactoryDeps = { - getProject: typeof getStream + getProject: GetStream addInviteDeclinedActivity: AddStreamInviteDeclinedActivity addProjectRole: typeof addOrUpdateStreamCollaborator } diff --git a/packages/server/modules/serverinvites/services/coreResourceCollection.ts b/packages/server/modules/serverinvites/services/coreResourceCollection.ts index 8f52945f6..f23c4ef04 100644 --- a/packages/server/modules/serverinvites/services/coreResourceCollection.ts +++ b/packages/server/modules/serverinvites/services/coreResourceCollection.ts @@ -1,4 +1,3 @@ -import { getStream } from '@/modules/core/repositories/streams' import { ProjectInviteResourceType, ServerInviteResourceType @@ -13,6 +12,7 @@ import { import { authorizeResolver } from '@/modules/shared' import { Roles } from '@speckle/shared' import { flatten } from 'lodash' +import { GetStream } from '@/modules/core/domain/streams/operations' const collectAndValidateServerTargetFactory = (): CollectAndValidateResourceTargets => (params) => { @@ -67,7 +67,7 @@ const collectAndValidateServerTargetFactory = } type CollectAndValidateProjectTargetFactoryDeps = { - getStream: typeof getStream + getStream: GetStream } const collectAndValidateProjectTargetFactory = diff --git a/packages/server/modules/serverinvites/services/projectInviteManagement.ts b/packages/server/modules/serverinvites/services/projectInviteManagement.ts index 888036150..a20f52b7b 100644 --- a/packages/server/modules/serverinvites/services/projectInviteManagement.ts +++ b/packages/server/modules/serverinvites/services/projectInviteManagement.ts @@ -1,3 +1,4 @@ +import { GetStream } from '@/modules/core/domain/streams/operations' import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types' import { MutationStreamInviteUseArgs, @@ -8,7 +9,6 @@ import { import { ContextResourceAccessRules } from '@/modules/core/helpers/token' import { LimitedUserRecord } from '@/modules/core/helpers/types' import { removePrivateFields } from '@/modules/core/helpers/userHelper' -import { getStream } from '@/modules/core/repositories/streams' import { getUser, getUsers } from '@/modules/core/repositories/users' import { ProjectInviteResourceType, @@ -58,7 +58,7 @@ const isStreamInviteCreateInput = ( ): i is StreamInviteCreateInput => has(i, 'streamId') export const createProjectInviteFactory = - (deps: { createAndSendInvite: CreateAndSendInvite; getStream: typeof getStream }) => + (deps: { createAndSendInvite: CreateAndSendInvite; getStream: GetStream }) => async (params: { input: StreamInviteCreateInput | FullProjectInviteCreateInput inviterId: string diff --git a/packages/server/modules/shared/index.ts b/packages/server/modules/shared/index.ts index 653cf8d52..c04e122ce 100644 --- a/packages/server/modules/shared/index.ts +++ b/packages/server/modules/shared/index.ts @@ -1,5 +1,5 @@ import { db } from '@/db/knex' -import { getStream } from '@/modules/core/repositories/streams' +import { getStreamFactory } from '@/modules/core/repositories/streams' import { adminOverrideEnabled } from '@/modules/shared/helpers/envHelper' import { getUserAclRoleFactory, @@ -29,6 +29,6 @@ export const authorizeResolver = authorizeResolverFactory({ getRoles: getRolesFactory({ db }), adminOverrideEnabled, getUserServerRole: getUserServerRoleFactory({ db }), - getStream, + getStream: getStreamFactory({ db }), getUserAclRole: getUserAclRoleFactory({ db }) }) diff --git a/packages/server/modules/shared/services/auth.ts b/packages/server/modules/shared/services/auth.ts index 41d3b4e97..da99780d6 100644 --- a/packages/server/modules/shared/services/auth.ts +++ b/packages/server/modules/shared/services/auth.ts @@ -1,9 +1,9 @@ +import { GetStream } from '@/modules/core/domain/streams/operations' import { isResourceAllowed, RoleResourceTargets, roleResourceTypeToTokenResourceType } from '@/modules/core/helpers/token' -import { getStream } from '@/modules/core/repositories/streams' import { AuthorizeResolver, GetUserAclRole, @@ -36,7 +36,7 @@ export const authorizeResolverFactory = getRoles: GetRoles adminOverrideEnabled: typeof adminOverrideEnabled getUserServerRole: GetUserServerRole - getStream: typeof getStream + getStream: GetStream getUserAclRole: GetUserAclRole }): AuthorizeResolver => async (userId, resourceId, requiredRole, userResourceAccessLimits) => { diff --git a/packages/server/modules/webhooks/services/webhooks.ts b/packages/server/modules/webhooks/services/webhooks.ts index 411824a7a..fd0c65be2 100644 --- a/packages/server/modules/webhooks/services/webhooks.ts +++ b/packages/server/modules/webhooks/services/webhooks.ts @@ -11,16 +11,14 @@ import { import { Webhook } from '@/modules/webhooks/domain/types' import { SetValuesNullable } from '@speckle/shared' import crs from 'crypto-random-string' -import { - StreamWithOptionalRole, - getStream as getStreamFn -} from '@/modules/core/repositories/streams' +import { StreamWithOptionalRole } from '@/modules/core/repositories/streams' import { getUser as getUserFn, UserWithOptionalRole } from '@/modules/core/repositories/users' import { Knex } from 'knex' import { ServerInfo } from '@/modules/core/helpers/types' +import { GetStream } from '@/modules/core/domain/streams/operations' const MAX_STREAM_WEBHOOKS = 100 @@ -110,7 +108,7 @@ export const dispatchStreamEventFactory = }: { db: Knex // TODO: this should not be injected here getServerInfo: typeof getServerInfoFn - getStream: typeof getStreamFn + getStream: GetStream createWebhookEvent: CreateWebhookEvent getUser: typeof getUserFn }) => diff --git a/packages/server/modules/webhooks/tests/webhooks.spec.js b/packages/server/modules/webhooks/tests/webhooks.spec.js index ee4fb75e7..7674e6380 100644 --- a/packages/server/modules/webhooks/tests/webhooks.spec.js +++ b/packages/server/modules/webhooks/tests/webhooks.spec.js @@ -31,9 +31,10 @@ const { } = require('@/modules/webhooks/services/webhooks') const { Users, Streams } = require('@/modules/core/dbSchema') const { getServerInfo } = require('@/modules/core/services/generic') -const { getStream } = require('@/modules/core/repositories/streams') const { getUser } = require('@/modules/core/repositories/users') +const { getStreamFactory } = require('@/modules/core/repositories/streams') +const getStream = getStreamFactory({ db }) const updateWebhook = updateWebhookFactory({ updateWebhookConfig: updateWebhookConfigFactory({ db }) }) diff --git a/packages/server/modules/workspaces/events/eventListener.ts b/packages/server/modules/workspaces/events/eventListener.ts index 246d4820f..f1de50b80 100644 --- a/packages/server/modules/workspaces/events/eventListener.ts +++ b/packages/server/modules/workspaces/events/eventListener.ts @@ -5,7 +5,7 @@ import { } from '@/modules/core/events/projectsEmitter' import { deleteProjectRoleFactory, - getStream, + getStreamFactory, upsertProjectRoleFactory } from '@/modules/core/repositories/streams' import { @@ -45,6 +45,7 @@ import { import { getStreams } from '@/modules/core/services/streams' import { withTransaction } from '@/modules/shared/helpers/dbHelper' import { findVerifiedEmailsByUserIdFactory } from '@/modules/core/repositories/userEmails' +import { GetStream } from '@/modules/core/domain/streams/operations' export const onProjectCreatedFactory = ({ @@ -89,7 +90,7 @@ export const onProjectCreatedFactory = export const onInviteFinalizedFactory = (deps: { - getStream: typeof getStream + getStream: GetStream logger: typeof logger updateWorkspaceRole: ReturnType }) => @@ -230,7 +231,7 @@ export const initializeEventListenersFactory = }), eventBus.listen(ServerInvitesEvents.Finalized, async ({ payload }) => { const onInviteFinalized = onInviteFinalizedFactory({ - getStream, + getStream: getStreamFactory({ db }), logger: moduleLogger, updateWorkspaceRole: updateWorkspaceRoleFactory({ getWorkspaceWithDomains: getWorkspaceWithDomainsFactory({ db }), diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index 77a320014..a86ef8cc0 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -4,12 +4,12 @@ import { removePrivateFields } from '@/modules/core/helpers/userHelper' import { getProjectCollaboratorsFactory, getProjectFactory, - getStream, getUserStreams, getUserStreamsCount, updateProjectFactory, upsertProjectRoleFactory, - getRolesByUserIdFactory + getRolesByUserIdFactory, + getStreamFactory } from '@/modules/core/repositories/streams' import { getUser, getUsers } from '@/modules/core/repositories/users' import { getStreams } from '@/modules/core/services/streams' @@ -136,6 +136,7 @@ import { renderEmail } from '@/modules/emails/services/emailRendering' import { sendEmail } from '@/modules/emails/services/sending' import { parseDefaultProjectRole } from '@/modules/workspaces/domain/logic' +const getStream = getStreamFactory({ db }) const requestNewEmailVerification = requestNewEmailVerificationFactory({ findEmail: findEmailFactory({ db }), getUser, @@ -724,7 +725,7 @@ export = FF_WORKSPACES_MODULE_ENABLED const trx = await db.transaction() const moveProjectToWorkspace = moveProjectToWorkspaceFactory({ - getProject: getProjectFactory(), + getProject: getProjectFactory({ db }), updateProject: updateProjectFactory({ db: trx }), upsertProjectRole: upsertProjectRoleFactory({ db: trx }), getProjectCollaborators: getProjectCollaboratorsFactory(), diff --git a/packages/server/modules/workspaces/services/invites.ts b/packages/server/modules/workspaces/services/invites.ts index 1665bf2de..415362b2d 100644 --- a/packages/server/modules/workspaces/services/invites.ts +++ b/packages/server/modules/workspaces/services/invites.ts @@ -71,7 +71,7 @@ import { anyEmailCompliantWithWorkspaceDomains, userEmailsCompliantWithWorkspaceDomains } from '@/modules/workspaces/domain/logic' -import { getStream } from '@/modules/core/repositories/streams' +import { GetStream } from '@/modules/core/domain/streams/operations' const isWorkspaceResourceTarget = ( target: InviteResourceTarget @@ -124,7 +124,7 @@ type CollectAndValidateWorkspaceTargetsFactoryDeps = getWorkspace: GetWorkspace getWorkspaceDomains: GetWorkspaceDomains findVerifiedEmailsByUserId: FindVerifiedEmailsByUserId - getStream: typeof getStream + getStream: GetStream } export const collectAndValidateWorkspaceTargetsFactory = diff --git a/packages/server/modules/workspaces/tests/helpers/creation.ts b/packages/server/modules/workspaces/tests/helpers/creation.ts index 567be93ba..a8ef6065c 100644 --- a/packages/server/modules/workspaces/tests/helpers/creation.ts +++ b/packages/server/modules/workspaces/tests/helpers/creation.ts @@ -1,5 +1,4 @@ import { db } from '@/db/knex' -import { getStream } from '@/modules/core/repositories/streams' import { findEmailsByUserIdFactory, findVerifiedEmailsByUserIdFactory @@ -45,6 +44,7 @@ import { StreamRoles, WorkspaceRoles } from '@speckle/shared' +import { getStreamFactory } from '@/modules/core/repositories/streams' export type BasicTestWorkspace = { /** @@ -207,6 +207,7 @@ export const createWorkspaceInviteDirectly = async ( args: CreateWorkspaceInviteMutationVariables, inviterId: string ) => { + const getStream = getStreamFactory({ db }) const createAndSendInvite = createAndSendInviteFactory({ findUserByTarget: findUserByTargetFactory(), insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), diff --git a/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts index f00302573..19fed22b2 100644 --- a/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/invites.graph.spec.ts @@ -65,7 +65,6 @@ import { import type { Express } from 'express' import { AllScopes } from '@/modules/core/helpers/mainConstants' import { getWorkspaceFactory } from '@/modules/workspaces/repositories/workspaces' -import { getStream } from '@/modules/core/repositories/streams' import { createUserEmailFactory, deleteUserEmailFactory, @@ -79,6 +78,7 @@ import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/s import { WorkspaceProtectedError } from '@/modules/workspaces/errors/workspace' import { ForbiddenError } from '@/modules/shared/errors' import cryptoRandomString from 'crypto-random-string' +import { getStreamFactory } from '@/modules/core/repositories/streams' enum InviteByTarget { Email = 'email', @@ -87,6 +87,8 @@ enum InviteByTarget { type TestGraphQLOperations = ReturnType +const getStream = getStreamFactory({ db }) + const buildGraphqlOperations = (deps: { apollo: TestApolloServer }) => { const { apollo } = deps diff --git a/packages/server/test/speckle-helpers/inviteHelper.ts b/packages/server/test/speckle-helpers/inviteHelper.ts index d80285dd9..47cd493fd 100644 --- a/packages/server/test/speckle-helpers/inviteHelper.ts +++ b/packages/server/test/speckle-helpers/inviteHelper.ts @@ -11,7 +11,6 @@ import { createAndSendInviteFactory } from '@/modules/serverinvites/services/cre import { BasicTestUser } from '@/test/authHelper' import { BasicTestStream } from '@/test/speckle-helpers/streamHelper' import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' -import { getStream } from '@/modules/core/repositories/streams' import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' import { getEventBus } from '@/modules/shared/services/eventBus' import { @@ -26,7 +25,9 @@ import { ServerInviteResourceTarget } from '@/modules/serverinvites/domain/types' import { EmailSendingServiceMock } from '@/test/mocks/global' +import { getStreamFactory } from '@/modules/core/repositories/streams' +const getStream = getStreamFactory({ db }) const createAndSendInvite = createAndSendInviteFactory({ findUserByTarget: findUserByTargetFactory(), insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), From 04b03cc3412efff79ea3354987cfd999db051056 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 8 Oct 2024 14:11:12 +0300 Subject: [PATCH 005/136] chore(server): IoC 30 - getCommitStream(s)Factory --- .../modules/core/domain/streams/operations.ts | 15 +++- .../modules/core/domain/streams/types.ts | 5 ++ .../modules/core/graph/resolvers/commits.js | 5 +- .../modules/core/graph/resolvers/versions.ts | 3 +- packages/server/modules/core/loaders.ts | 5 +- .../modules/core/repositories/streams.ts | 90 ++++++++++--------- .../core/services/commit/management.ts | 9 +- .../server/modules/core/tests/commits.spec.js | 5 +- 8 files changed, 83 insertions(+), 54 deletions(-) diff --git a/packages/server/modules/core/domain/streams/operations.ts b/packages/server/modules/core/domain/streams/operations.ts index 4ab040bef..880f83a8a 100644 --- a/packages/server/modules/core/domain/streams/operations.ts +++ b/packages/server/modules/core/domain/streams/operations.ts @@ -1,4 +1,7 @@ -import { StreamWithOptionalRole } from '@/modules/core/domain/streams/types' +import { + StreamWithCommitId, + StreamWithOptionalRole +} from '@/modules/core/domain/streams/types' import { Optional } from '@speckle/shared' import { Knex } from 'knex' @@ -19,3 +22,13 @@ export type GetStream = ( trx: Knex.Transaction }> ) => Promise> + +export type GetCommitStreams = (params: { + commitIds: string[] + userId?: string +}) => Promise + +export type GetCommitStream = (params: { + commitId: string + userId?: string +}) => Promise> diff --git a/packages/server/modules/core/domain/streams/types.ts b/packages/server/modules/core/domain/streams/types.ts index 825b475da..e27876ea0 100644 --- a/packages/server/modules/core/domain/streams/types.ts +++ b/packages/server/modules/core/domain/streams/types.ts @@ -10,3 +10,8 @@ export type StreamWithOptionalRole = Stream & { */ role?: StreamRoles } + +export type StreamWithCommitId = + StreamType & { + commitId: string + } diff --git a/packages/server/modules/core/graph/resolvers/commits.js b/packages/server/modules/core/graph/resolvers/commits.js index 842b7b69f..424e1e261 100644 --- a/packages/server/modules/core/graph/resolvers/commits.js +++ b/packages/server/modules/core/graph/resolvers/commits.js @@ -57,9 +57,9 @@ const { const { db } = require('@/db/knex') const { markCommitStreamUpdated, - getCommitStream, getStreamFactory, - getStreamsFactory + getStreamsFactory, + getCommitStreamFactory } = require('@/modules/core/repositories/streams') const { markCommitBranchUpdatedFactory, @@ -81,6 +81,7 @@ const COMMIT_CREATED = CommitPubsubEvents.CommitCreated const COMMIT_UPDATED = CommitPubsubEvents.CommitUpdated const COMMIT_DELETED = CommitPubsubEvents.CommitDeleted +const getCommitStream = getCommitStreamFactory({ db }) const getStream = getStreamFactory({ db }) const getStreams = getStreamsFactory({ db }) const deleteCommitAndNotify = deleteCommitAndNotifyFactory({ diff --git a/packages/server/modules/core/graph/resolvers/versions.ts b/packages/server/modules/core/graph/resolvers/versions.ts index 4c9a94cad..114cc1386 100644 --- a/packages/server/modules/core/graph/resolvers/versions.ts +++ b/packages/server/modules/core/graph/resolvers/versions.ts @@ -40,7 +40,7 @@ import { markCommitBranchUpdatedFactory } from '@/modules/core/repositories/branches' import { - getCommitStream, + getCommitStreamFactory, getStreamFactory, getStreamsFactory, markCommitStreamUpdated @@ -53,6 +53,7 @@ import { } from '@/modules/activitystream/services/commitActivity' import { getObjectFactory } from '@/modules/core/repositories/objects' +const getCommitStream = getCommitStreamFactory({ db }) const getStream = getStreamFactory({ db }) const getStreams = getStreamsFactory({ db }) const getObject = getObjectFactory({ db }) diff --git a/packages/server/modules/core/loaders.ts b/packages/server/modules/core/loaders.ts index 9ff4e97eb..6a46c03c1 100644 --- a/packages/server/modules/core/loaders.ts +++ b/packages/server/modules/core/loaders.ts @@ -5,10 +5,10 @@ import { getOwnedFavoritesCountByUserIds, getStreamRoles, getStreamsSourceApps, - getCommitStreams, StreamWithCommitId, getUserStreamCounts, - getStreamsFactory + getStreamsFactory, + getCommitStreamsFactory } from '@/modules/core/repositories/streams' import { UserWithOptionalRole, getUsers } from '@/modules/core/repositories/users' import { keyBy } from 'lodash' @@ -117,6 +117,7 @@ const getCommitBranches = getCommitBranchesFactory({ db }) const getStreamCommitCounts = getStreamCommitCountsFactory({ db }) const getUserStreamCommitCounts = getUserStreamCommitCountsFactory({ db }) const getUserAuthoredCommitCounts = getUserAuthoredCommitCountsFactory({ db }) +const getCommitStreams = getCommitStreamsFactory({ db }) /** * TODO: Lazy load DataLoaders to reduce memory usage diff --git a/packages/server/modules/core/repositories/streams.ts b/packages/server/modules/core/repositories/streams.ts index bd1f1aaf0..7a775ee23 100644 --- a/packages/server/modules/core/repositories/streams.ts +++ b/packages/server/modules/core/repositories/streams.ts @@ -25,6 +25,7 @@ import { Roles, StreamRoles } from '@/modules/core/helpers/mainConstants' import { LimitedUserRecord, StreamAclRecord, + StreamCommitRecord, StreamFavoriteRecord, StreamRecord, UserWithRole @@ -66,13 +67,22 @@ import { GetRolesByUserId, UpsertProjectRole } from '@/modules/core/domain/projects/operations' -import { StreamWithOptionalRole } from '@/modules/core/domain/streams/types' -import { GetStream, GetStreams } from '@/modules/core/domain/streams/operations' -export type { StreamWithOptionalRole } +import { + StreamWithCommitId, + StreamWithOptionalRole +} from '@/modules/core/domain/streams/types' +import { + GetCommitStream, + GetCommitStreams, + GetStream, + GetStreams +} from '@/modules/core/domain/streams/operations' +export type { StreamWithOptionalRole, StreamWithCommitId } const tables = { streams: (db: Knex) => db(Streams.name), - streamAcl: (db: Knex) => db(StreamAcl.name) + streamAcl: (db: Knex) => db(StreamAcl.name), + streamCommits: (db: Knex) => db(StreamCommits.name) } /** @@ -190,48 +200,48 @@ export const getProjectFactory = return project } -export type StreamWithCommitId = StreamWithOptionalRole & { commitId: string } +export const getCommitStreamsFactory = + (deps: { db: Knex }): GetCommitStreams => + async (params: { commitIds: string[]; userId?: string }) => { + const { commitIds, userId } = params + if (!commitIds?.length) return [] -export async function getCommitStreams(params: { - commitIds: string[] - userId?: string -}) { - const { commitIds, userId } = params - if (!commitIds?.length) return [] + const q = tables + .streamCommits(deps.db) + .select>([...Streams.cols, StreamCommits.col.commitId]) + .innerJoin(Streams.name, Streams.col.id, StreamCommits.col.streamId) + .whereIn(StreamCommits.col.commitId, commitIds) - const q = StreamCommits.knex() - .select>([...Streams.cols, StreamCommits.col.commitId]) - .innerJoin(Streams.name, Streams.col.id, StreamCommits.col.streamId) - .whereIn(StreamCommits.col.commitId, commitIds) + if (userId) { + q.select([ + // Getting first role from grouped results + knex.raw(`(array_agg("stream_acl"."role"))[1] as role`) + ]) + q.leftJoin(StreamAcl.name, function () { + this.on(StreamAcl.col.resourceId, Streams.col.id).andOnVal( + StreamAcl.col.userId, + userId + ) + }) + q.groupBy(Streams.col.id, StreamCommits.col.commitId) + } - if (userId) { - q.select([ - // Getting first role from grouped results - knex.raw(`(array_agg("stream_acl"."role"))[1] as role`) - ]) - q.leftJoin(StreamAcl.name, function () { - this.on(StreamAcl.col.resourceId, Streams.col.id).andOnVal( - StreamAcl.col.userId, - userId - ) - }) - q.groupBy(Streams.col.id, StreamCommits.col.commitId) + const results = await q + return results } - const results = await q - return results -} +export const getCommitStreamFactory = + (deps: { db: Knex }): GetCommitStream => + async (params: { commitId: string; userId?: string }) => { + const { commitId } = params + if (!commitId) throw new InvalidArgumentError('Invalid commit ID') -export async function getCommitStream(params: { commitId: string; userId?: string }) { - const { commitId } = params - if (!commitId) throw new InvalidArgumentError('Invalid commit ID') - - const results = await getCommitStreams({ - commitIds: [commitId], - userId: params.userId - }) - return >results[0] -} + const results = await getCommitStreamsFactory(deps)({ + commitIds: [commitId], + userId: params.userId + }) + return >results[0] + } /** * Get base query for finding or counting user favorited streams diff --git a/packages/server/modules/core/services/commit/management.ts b/packages/server/modules/core/services/commit/management.ts index 1f41ee9a3..a48acead8 100644 --- a/packages/server/modules/core/services/commit/management.ts +++ b/packages/server/modules/core/services/commit/management.ts @@ -25,7 +25,7 @@ import { UpdateCommitAndNotify } from '@/modules/core/domain/commits/operations' import { GetObject } from '@/modules/core/domain/objects/operations' -import { GetStream } from '@/modules/core/domain/streams/operations' +import { GetCommitStream, GetStream } from '@/modules/core/domain/streams/operations' import { CommitCreateError, CommitDeleteError, @@ -44,10 +44,7 @@ import { } from '@/modules/core/graph/generated/graphql' import { CommitRecord } from '@/modules/core/helpers/types' import { getCommitFactory } from '@/modules/core/repositories/commits' -import { - getCommitStream, - markCommitStreamUpdated -} from '@/modules/core/repositories/streams' +import { markCommitStreamUpdated } from '@/modules/core/repositories/streams' import { ensureError, MaybeNullOrUndefined, Nullable, Roles } from '@speckle/shared' import { has } from 'lodash' @@ -254,7 +251,7 @@ export const updateCommitAndNotifyFactory = (deps: { getCommit: GetCommit getStream: GetStream - getCommitStream: typeof getCommitStream + getCommitStream: GetCommitStream getStreamBranchByName: GetStreamBranchByName getCommitBranch: GetCommitBranch switchCommitBranch: SwitchCommitBranch diff --git a/packages/server/modules/core/tests/commits.spec.js b/packages/server/modules/core/tests/commits.spec.js index 238caa564..bc5a0f20e 100644 --- a/packages/server/modules/core/tests/commits.spec.js +++ b/packages/server/modules/core/tests/commits.spec.js @@ -46,8 +46,8 @@ const { } = require('@/modules/core/services/commit/management') const { markCommitStreamUpdated, - getCommitStream, - getStreamFactory + getStreamFactory, + getCommitStreamFactory } = require('@/modules/core/repositories/streams') const { addCommitDeletedActivity, @@ -57,6 +57,7 @@ const { const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const getCommitStream = getCommitStreamFactory({ db }) const getStream = getStreamFactory({ db }) const createBranch = createBranchFactory({ db }) const createBranchAndNotify = createBranchAndNotifyFactory({ From 380fe049e66e7e59131874d49792d4b26c89687c Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 8 Oct 2024 14:43:19 +0300 Subject: [PATCH 006/136] chore(server): IoC 31 - getStreamCollaboratorsFactory --- .../server/modules/accessrequests/index.ts | 5 +- .../accessrequests/services/eventListener.ts | 4 +- .../tests/projectAccessRequests.spec.ts | 3 +- .../tests/streamAccessRequests.spec.ts | 3 +- .../activitystream/services/streamActivity.ts | 3 +- .../modules/cli/commands/download/commit.ts | 3 +- .../modules/cli/commands/download/project.ts | 3 +- .../modules/core/domain/streams/operations.ts | 10 +++- .../modules/core/domain/streams/types.ts | 10 +++- .../modules/core/graph/resolvers/projects.ts | 5 +- .../modules/core/repositories/streams.ts | 50 ++++++++++--------- .../server/modules/cross-server-sync/index.ts | 4 +- .../cross-server-sync/services/commit.ts | 8 +-- .../workspaces/graph/resolvers/workspaces.ts | 2 +- 14 files changed, 70 insertions(+), 43 deletions(-) diff --git a/packages/server/modules/accessrequests/index.ts b/packages/server/modules/accessrequests/index.ts index 25232eac8..abcec1b3c 100644 --- a/packages/server/modules/accessrequests/index.ts +++ b/packages/server/modules/accessrequests/index.ts @@ -1,7 +1,8 @@ +import { db } from '@/db/knex' import { moduleLogger } from '@/logging/logging' import { AccessRequestsEmitter } from '@/modules/accessrequests/events/emitter' import { initializeEventListenerFactory } from '@/modules/accessrequests/services/eventListener' -import { getStreamCollaborators } from '@/modules/core/repositories/streams' +import { getStreamCollaboratorsFactory } from '@/modules/core/repositories/streams' import { publishNotification } from '@/modules/notifications/services/publication' import { Optional, SpeckleModule } from '@/modules/shared/helpers/typeHelper' @@ -13,7 +14,7 @@ const ServerAccessRequestsModule: SpeckleModule = { if (isInitial) { const initializeEventListener = initializeEventListenerFactory({ - getStreamCollaborators, + getStreamCollaborators: getStreamCollaboratorsFactory({ db }), publishNotification, accessRequestsEventListener: AccessRequestsEmitter.listen }) diff --git a/packages/server/modules/accessrequests/services/eventListener.ts b/packages/server/modules/accessrequests/services/eventListener.ts index 52c4f1fe0..9f16ed0b3 100644 --- a/packages/server/modules/accessrequests/services/eventListener.ts +++ b/packages/server/modules/accessrequests/services/eventListener.ts @@ -4,15 +4,15 @@ import { AccessRequestsEventsPayloads } from '@/modules/accessrequests/events/emitter' import { isStreamAccessRequest } from '@/modules/accessrequests/repositories' +import { GetStreamCollaborators } from '@/modules/core/domain/streams/operations' import { Roles } from '@/modules/core/helpers/mainConstants' -import { getStreamCollaborators } from '@/modules/core/repositories/streams' import { NotificationPublisher, NotificationType } from '@/modules/notifications/helpers/types' type OnServerAccessRequestCreatedDeps = { - getStreamCollaborators: typeof getStreamCollaborators + getStreamCollaborators: GetStreamCollaborators publishNotification: NotificationPublisher } diff --git a/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts b/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts index 69b003947..225b5641d 100644 --- a/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts +++ b/packages/server/modules/accessrequests/tests/projectAccessRequests.spec.ts @@ -22,7 +22,7 @@ import { StreamAccessUpdateError } from '@/modules/core/errors/stream' import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes' import { Roles } from '@/modules/core/helpers/mainConstants' import { - getStreamCollaborators, + getStreamCollaboratorsFactory, getStreamFactory } from '@/modules/core/repositories/streams' import { @@ -52,6 +52,7 @@ import { expect } from 'chai' import { noop } from 'lodash' const getStream = getStreamFactory({ db }) +const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) const requestProjectAccess = requestProjectAccessFactory({ getUserStreamAccessRequest: getUserStreamAccessRequestFactory({ getUserProjectAccessRequest: getUserProjectAccessRequestFactory({ diff --git a/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts b/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts index c124b16f4..60a7d829e 100644 --- a/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts +++ b/packages/server/modules/accessrequests/tests/streamAccessRequests.spec.ts @@ -24,7 +24,7 @@ import { StreamAccessUpdateError } from '@/modules/core/errors/stream' import { mapStreamRoleToValue } from '@/modules/core/helpers/graphTypes' import { Roles } from '@/modules/core/helpers/mainConstants' import { - getStreamCollaborators, + getStreamCollaboratorsFactory, getStreamFactory } from '@/modules/core/repositories/streams' import { @@ -53,6 +53,7 @@ import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/strea import { expect } from 'chai' import { noop } from 'lodash' +const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) const getStream = getStreamFactory({ db }) const requestStreamAccess = requestStreamAccessFactory({ requestProjectAccess: requestProjectAccessFactory({ diff --git a/packages/server/modules/activitystream/services/streamActivity.ts b/packages/server/modules/activitystream/services/streamActivity.ts index 1e3ca7511..1ff7c8af1 100644 --- a/packages/server/modules/activitystream/services/streamActivity.ts +++ b/packages/server/modules/activitystream/services/streamActivity.ts @@ -7,7 +7,6 @@ import { } from '@/modules/shared/utils/subscriptions' import { StreamCreateInput } from '@/test/graphql/generated/graphql' import { Knex } from 'knex' -import { getStreamCollaborators } from '@/modules/core/repositories/streams' import { chunk, flatten } from 'lodash' import { StreamRecord } from '@/modules/core/helpers/types' import { @@ -30,6 +29,7 @@ import { AddStreamInviteSentOutActivity, SaveActivity } from '@/modules/activitystream/domain/operations' +import { getStreamCollaboratorsFactory } from '@/modules/core/repositories/streams' /** * Save "stream updated" activity @@ -94,6 +94,7 @@ export async function addStreamDeletedActivity(params: { ]) // Notify all stream users + const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) const users = await getStreamCollaborators(streamId) const userBatches = chunk(users, 15) for (const userBatch of userBatches) { diff --git a/packages/server/modules/cli/commands/download/commit.ts b/packages/server/modules/cli/commands/download/commit.ts index 9cf4bb30f..54c43fed2 100644 --- a/packages/server/modules/cli/commands/download/commit.ts +++ b/packages/server/modules/cli/commands/download/commit.ts @@ -2,7 +2,7 @@ import { CommandModule } from 'yargs' import { downloadCommitFactory } from '@/modules/cross-server-sync/services/commit' import { cliLogger } from '@/logging/logging' import { - getStreamCollaborators, + getStreamCollaboratorsFactory, getStreamFactory, markCommitStreamUpdated } from '@/modules/core/repositories/streams' @@ -143,6 +143,7 @@ const command: CommandModule< addCommitCreatedActivity }) + const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) const downloadCommit = downloadCommitFactory({ getStream, getStreamBranchByName: getStreamBranchByNameFactory({ db }), diff --git a/packages/server/modules/cli/commands/download/project.ts b/packages/server/modules/cli/commands/download/project.ts index 35b9bb2b8..53edb4505 100644 --- a/packages/server/modules/cli/commands/download/project.ts +++ b/packages/server/modules/cli/commands/download/project.ts @@ -3,7 +3,7 @@ import { cliLogger } from '@/logging/logging' import { downloadProjectFactory } from '@/modules/cross-server-sync/services/project' import { downloadCommitFactory } from '@/modules/cross-server-sync/services/commit' import { - getStreamCollaborators, + getStreamCollaboratorsFactory, getStreamFactory, markCommitStreamUpdated } from '@/modules/core/repositories/streams' @@ -140,6 +140,7 @@ const command: CommandModule< addCommitCreatedActivity }) + const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) const getStreamBranchByName = getStreamBranchByNameFactory({ db }) const downloadProject = downloadProjectFactory({ downloadCommit: downloadCommitFactory({ diff --git a/packages/server/modules/core/domain/streams/operations.ts b/packages/server/modules/core/domain/streams/operations.ts index 880f83a8a..6fd50654a 100644 --- a/packages/server/modules/core/domain/streams/operations.ts +++ b/packages/server/modules/core/domain/streams/operations.ts @@ -1,8 +1,9 @@ import { StreamWithCommitId, - StreamWithOptionalRole + StreamWithOptionalRole, + LimitedUserWithStreamRole } from '@/modules/core/domain/streams/types' -import { Optional } from '@speckle/shared' +import { Optional, StreamRoles } from '@speckle/shared' import { Knex } from 'knex' export type GetStreams = ( @@ -32,3 +33,8 @@ export type GetCommitStream = (params: { commitId: string userId?: string }) => Promise> + +export type GetStreamCollaborators = ( + streamId: string, + type?: StreamRoles +) => Promise> diff --git a/packages/server/modules/core/domain/streams/types.ts b/packages/server/modules/core/domain/streams/types.ts index e27876ea0..2a8f4e7ed 100644 --- a/packages/server/modules/core/domain/streams/types.ts +++ b/packages/server/modules/core/domain/streams/types.ts @@ -1,4 +1,8 @@ -import { StreamRecord } from '@/modules/core/helpers/types' +import { + LimitedUserRecord, + StreamRecord, + UserWithRole +} from '@/modules/core/helpers/types' import { StreamRoles } from '@speckle/shared' export type Stream = StreamRecord @@ -15,3 +19,7 @@ export type StreamWithCommitId & { + streamRole: StreamRoles +} diff --git a/packages/server/modules/core/graph/resolvers/projects.ts b/packages/server/modules/core/graph/resolvers/projects.ts index 364626335..0ff1b19d9 100644 --- a/packages/server/modules/core/graph/resolvers/projects.ts +++ b/packages/server/modules/core/graph/resolvers/projects.ts @@ -13,8 +13,8 @@ import { isResourceAllowed, toProjectIdWhitelist } from '@/modules/core/helpers/ import { getUserStreamsCount, getUserStreams, - getStreamCollaborators, - getStreamFactory + getStreamFactory, + getStreamCollaboratorsFactory } from '@/modules/core/repositories/streams' import { getRateLimitResult, @@ -38,6 +38,7 @@ import { import { has } from 'lodash' const getStream = getStreamFactory({ db }) +const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) export = { Query: { diff --git a/packages/server/modules/core/repositories/streams.ts b/packages/server/modules/core/repositories/streams.ts index 7a775ee23..a123f8ee4 100644 --- a/packages/server/modules/core/repositories/streams.ts +++ b/packages/server/modules/core/repositories/streams.ts @@ -75,6 +75,7 @@ import { GetCommitStream, GetCommitStreams, GetStream, + GetStreamCollaborators, GetStreams } from '@/modules/core/domain/streams/operations' export type { StreamWithOptionalRole, StreamWithCommitId } @@ -653,35 +654,38 @@ export async function getDiscoverableStreams(params: GetDiscoverableStreamsParam /** * Get all stream collaborators. Optionally filter only specific roles. */ -export async function getStreamCollaborators(streamId: string, type?: StreamRoles) { - const q = StreamAcl.knex() - .select>([ - ...Users.cols, - knex.raw(`${StreamAcl.col.role} as "streamRole"`), - knex.raw(`(array_agg(${ServerAcl.col.role}))[1] as "role"`) - ]) - .where(StreamAcl.col.resourceId, streamId) - .innerJoin(Users.name, Users.col.id, StreamAcl.col.userId) - .innerJoin(ServerAcl.name, ServerAcl.col.userId, Users.col.id) - .groupBy(Users.col.id, StreamAcl.col.role) +export const getStreamCollaboratorsFactory = + (deps: { db: Knex }): GetStreamCollaborators => + async (streamId: string, type?: StreamRoles) => { + const q = tables + .streamAcl(deps.db) + .select>([ + ...Users.cols, + knex.raw(`${StreamAcl.col.role} as "streamRole"`), + knex.raw(`(array_agg(${ServerAcl.col.role}))[1] as "role"`) + ]) + .where(StreamAcl.col.resourceId, streamId) + .innerJoin(Users.name, Users.col.id, StreamAcl.col.userId) + .innerJoin(ServerAcl.name, ServerAcl.col.userId, Users.col.id) + .groupBy(Users.col.id, StreamAcl.col.role) - if (type) { - q.andWhere(StreamAcl.col.role, type) + if (type) { + q.andWhere(StreamAcl.col.role, type) + } + + const items = (await q).map((i) => ({ + ...removePrivateFields(i), + streamRole: i.streamRole, + role: i.role + })) + return items } - const items = (await q).map((i) => ({ - ...removePrivateFields(i), - streamRole: i.streamRole, - role: i.role - })) - return items -} - // TODO: Inject db export const getProjectCollaboratorsFactory = - (): GetProjectCollaborators => + (deps: { db: Knex }): GetProjectCollaborators => async ({ projectId }) => { - return await getStreamCollaborators(projectId) + return await getStreamCollaboratorsFactory(deps)(projectId) } type BaseUserStreamsQueryParams = { diff --git a/packages/server/modules/cross-server-sync/index.ts b/packages/server/modules/cross-server-sync/index.ts index 925d679e7..4bd195b59 100644 --- a/packages/server/modules/cross-server-sync/index.ts +++ b/packages/server/modules/cross-server-sync/index.ts @@ -42,7 +42,7 @@ import { } from '@/modules/core/repositories/objects' import { getOnboardingBaseStream, - getStreamCollaborators, + getStreamCollaboratorsFactory, getStreamFactory, markCommitStreamUpdated, markOnboardingBaseStream @@ -124,7 +124,7 @@ const crossServerSyncModule: SpeckleModule = { downloadCommit: downloadCommitFactory({ getStream, getStreamBranchByName, - getStreamCollaborators, + getStreamCollaborators: getStreamCollaboratorsFactory({ db }), getUser, createCommitByBranchId, createObject, diff --git a/packages/server/modules/cross-server-sync/services/commit.ts b/packages/server/modules/cross-server-sync/services/commit.ts index 12f838689..86619db9b 100644 --- a/packages/server/modules/cross-server-sync/services/commit.ts +++ b/packages/server/modules/cross-server-sync/services/commit.ts @@ -2,7 +2,6 @@ import fetch from 'cross-fetch' import { ApolloClient, NormalizedCacheObject, gql } from '@apollo/client/core' import { getFrontendOrigin } from '@/modules/shared/helpers/envHelper' import { CreateCommentInput } from '@/test/graphql/generated/graphql' -import { getStreamCollaborators } from '@/modules/core/repositories/streams' import { Roles, timeoutAt } from '@speckle/shared' import { createObject } from '@/modules/core/services/objects' import ObjectLoader from '@speckle/objectloader' @@ -31,7 +30,10 @@ import { import { GetStreamBranchByName } from '@/modules/core/domain/branches/operations' import { CreateCommitByBranchId } from '@/modules/core/domain/commits/operations' import { GetObject } from '@/modules/core/domain/objects/operations' -import { GetStream } from '@/modules/core/domain/streams/operations' +import { + GetStream, + GetStreamCollaborators +} from '@/modules/core/domain/streams/operations' type LocalResources = Awaited>> type LocalResourcesWithCommit = LocalResources & { newCommitId: string } @@ -221,7 +223,7 @@ const parseIncomingUrl = async (url: string, token?: string) => { type GetLocalResourcesDeps = { getStream: GetStream getStreamBranchByName: GetStreamBranchByName - getStreamCollaborators: typeof getStreamCollaborators + getStreamCollaborators: GetStreamCollaborators getUser: typeof getUser } diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index a86ef8cc0..09a830b0e 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -728,7 +728,7 @@ export = FF_WORKSPACES_MODULE_ENABLED getProject: getProjectFactory({ db }), updateProject: updateProjectFactory({ db: trx }), upsertProjectRole: upsertProjectRoleFactory({ db: trx }), - getProjectCollaborators: getProjectCollaboratorsFactory(), + getProjectCollaborators: getProjectCollaboratorsFactory({ db }), getWorkspaceRoles: getWorkspaceRolesFactory({ db: trx }), getWorkspaceRoleToDefaultProjectRoleMapping: getWorkspaceRoleToDefaultProjectRoleMappingFactory({ From aa4f31927712cbeaf993a6c31bac1bc1cdebd581 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Wed, 9 Oct 2024 09:10:20 +0100 Subject: [PATCH 007/136] chore(server): IoC 32 - createStreamFactory (#3204) --- .../tests/activitySummary.spec.ts | 55 +++++- .../server/modules/auth/tests/auth.spec.js | 66 ++++++- .../tests/blobstorage.graph.spec.js | 67 ++++++- .../tests/blobstorage.integration.spec.js | 67 ++++++- .../modules/cli/commands/download/project.ts | 46 ++++- .../comments/tests/comments.graph.spec.js | 70 +++++++- .../modules/comments/tests/comments.spec.js | 66 ++++++- .../modules/core/domain/streams/operations.ts | 32 +++- .../modules/core/events/projectsEmitter.ts | 2 + .../modules/core/graph/resolvers/projects.ts | 47 ++++- .../modules/core/graph/resolvers/streams.ts | 52 +++++- .../modules/core/repositories/streams.ts | 113 ++++++------ .../server/modules/core/services/streams.js | 15 -- .../modules/core/services/streams/clone.ts | 3 +- .../core/services/streams/management.ts | 169 +++++++++--------- .../core/services/streams/onboarding.ts | 50 +++++- .../modules/core/tests/branches.spec.js | 63 ++++++- .../server/modules/core/tests/commits.spec.js | 63 ++++++- .../core/tests/favoriteStreams.spec.js | 67 ++++++- .../server/modules/core/tests/generic.spec.js | 67 ++++++- .../server/modules/core/tests/objects.spec.js | 67 ++++++- .../server/modules/core/tests/rest.spec.js | 67 ++++++- .../server/modules/core/tests/streams.spec.ts | 51 +++++- .../server/modules/core/tests/users.spec.js | 64 ++++++- .../modules/core/tests/usersAdminList.spec.ts | 56 +++++- .../server/modules/cross-server-sync/index.ts | 45 ++++- .../cross-server-sync/services/project.ts | 4 +- .../tests/fileuploads.integration.spec.ts | 57 +++++- .../server/modules/stats/tests/stats.spec.ts | 58 +++++- .../modules/webhooks/tests/cleanup.spec.ts | 58 +++++- .../modules/webhooks/tests/webhooks.spec.js | 67 ++++++- packages/server/scripts/streamObjects.js | 67 ++++++- .../test/speckle-helpers/streamHelper.ts | 57 +++++- 33 files changed, 1696 insertions(+), 202 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activitySummary.spec.ts b/packages/server/modules/activitystream/tests/activitySummary.spec.ts index 34b6ebb72..95f820d29 100644 --- a/packages/server/modules/activitystream/tests/activitySummary.spec.ts +++ b/packages/server/modules/activitystream/tests/activitySummary.spec.ts @@ -6,7 +6,7 @@ import { sendActivityNotificationsFactory } from '@/modules/activitystream/services/summary' import { expect } from 'chai' -import { createStream, deleteStream } from '@/modules/core/services/streams' +import { deleteStream } from '@/modules/core/services/streams' import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types' import { ActivityDigestMessage, @@ -19,7 +19,28 @@ import { saveActivityFactory } from '@/modules/activitystream/repositories' import { db } from '@/db/knex' -import { getStreamFactory } from '@/modules/core/repositories/streams' +import { + createStreamFactory, + getStreamFactory +} from '@/modules/core/repositories/streams' +import { + createStreamReturnRecordFactory, + legacyCreateStreamFactory +} from '@/modules/core/services/streams/management' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { getUsers } from '@/modules/core/repositories/users' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { createBranchFactory } from '@/modules/core/repositories/branches' +import { publish } from '@/modules/shared/utils/subscriptions' const cleanup = async () => { await truncateTables([StreamActivity.name, Users.name]) @@ -31,6 +52,36 @@ const createActivitySummary = createActivitySummaryFactory({ getStream, getActivity: getActivityFactory({ db }) }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) describe('Activity summary @activity', () => { const userA: BasicTestUser = { diff --git a/packages/server/modules/auth/tests/auth.spec.js b/packages/server/modules/auth/tests/auth.spec.js index ab6c8a0b7..5efae8808 100644 --- a/packages/server/modules/auth/tests/auth.spec.js +++ b/packages/server/modules/auth/tests/auth.spec.js @@ -2,7 +2,6 @@ const crs = require('crypto-random-string') const chai = require('chai') const request = require('supertest') const { createUser } = require('@/modules/core/services/users') -const { createStream } = require('@/modules/core/services/streams') const { updateServerInfo } = require('@/modules/core/services/generic') const { getUserByEmail } = require('@/modules/core/services/users') @@ -12,12 +11,75 @@ const { beforeEachContext, initializeTestServer } = require('@/test/hooks') const { createStreamInviteDirectly } = require('@/test/speckle-helpers/inviteHelper') const { RateLimiterMemory } = require('rate-limiter-flexible') const { - findInviteFactory + findInviteFactory, + findUserByTargetFactory, + insertInviteAndDeleteOldFactory } = require('@/modules/serverinvites/repositories/serverInvites') const db = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) const createInviteDirectly = createStreamInviteDirectly const findInvite = findInviteFactory({ db }) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + const expect = chai.expect let app diff --git a/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js b/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js index 920e24def..68f82035b 100644 --- a/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js +++ b/packages/server/modules/blobstorage/tests/blobstorage.graph.spec.js @@ -1,12 +1,77 @@ const { buildApolloServer } = require('@/app') const { truncateTables } = require('@/test/hooks') -const { createStream } = require('@/modules/core/services/streams') const { createUser } = require('@/modules/core/services/users') const { gql } = require('graphql-tag') const { createBlobs } = require('@/modules/blobstorage/tests/helpers') const { expect } = require('chai') const { Users, Streams } = require('@/modules/core/dbSchema') const { createAuthedTestContext, executeOperation } = require('@/test/graphqlHelper') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') + +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) describe('Blobs graphql @blobstorage', () => { /** @type {import('@/test/graphqlHelper').ServerAndContext} */ diff --git a/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js b/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js index f12bf600a..a33f7e6bc 100644 --- a/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js +++ b/packages/server/modules/blobstorage/tests/blobstorage.integration.spec.js @@ -2,10 +2,75 @@ const { Buffer } = require('node:buffer') const request = require('supertest') const expect = require('chai').expect const { beforeEachContext } = require('@/test/hooks') -const { createStream } = require('@/modules/core/services/streams') const { createToken } = require('@/modules/core/services/tokens') const { createUser } = require('@/modules/core/services/users') const { Scopes } = require('@/modules/core/helpers/mainConstants') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') + +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) describe('Blobs integration @blobstorage', () => { let app diff --git a/packages/server/modules/cli/commands/download/project.ts b/packages/server/modules/cli/commands/download/project.ts index 53edb4505..354ffb422 100644 --- a/packages/server/modules/cli/commands/download/project.ts +++ b/packages/server/modules/cli/commands/download/project.ts @@ -3,6 +3,7 @@ import { cliLogger } from '@/logging/logging' import { downloadProjectFactory } from '@/modules/cross-server-sync/services/project' import { downloadCommitFactory } from '@/modules/cross-server-sync/services/commit' import { + createStreamFactory, getStreamCollaboratorsFactory, getStreamFactory, markCommitStreamUpdated @@ -15,7 +16,7 @@ import { getStreamBranchesByNameFactory, markCommitBranchUpdatedFactory } from '@/modules/core/repositories/branches' -import { getUser } from '@/modules/core/repositories/users' +import { getUser, getUsers } from '@/modules/core/repositories/users' import { createCommitByBranchIdFactory } from '@/modules/core/services/commit/management' import { createObject } from '@/modules/core/services/objects' import { @@ -26,7 +27,6 @@ import { createCommentReplyAndNotifyFactory, createCommentThreadAndNotifyFactory } from '@/modules/comments/services/management' -import { createStreamReturnRecord } from '@/modules/core/services/streams/management' import { createBranchAndNotifyFactory } from '@/modules/core/services/branch/management' import { CommentsEmitter } from '@/modules/comments/events/emitter' import { @@ -57,6 +57,20 @@ import { validateInputAttachmentsFactory } from '@/modules/comments/services/com import { addBranchCreatedActivity } from '@/modules/activitystream/services/branchActivity' import { VersionsEmitter } from '@/modules/core/events/versionsEmitter' import { addCommitCreatedActivity } from '@/modules/activitystream/services/commitActivity' +import { createStreamReturnRecordFactory } from '@/modules/core/services/streams/management' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' const command: CommandModule< unknown, @@ -140,6 +154,34 @@ const command: CommandModule< addCommitCreatedActivity }) + const createStreamReturnRecord = createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity: addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish + }), + projectsEventsEmitter: ProjectsEmitter.emit + }) + const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) const getStreamBranchByName = getStreamBranchByNameFactory({ db }) const downloadProject = downloadProjectFactory({ diff --git a/packages/server/modules/comments/tests/comments.graph.spec.js b/packages/server/modules/comments/tests/comments.graph.spec.js index 58aa2d277..cd175eb0a 100644 --- a/packages/server/modules/comments/tests/comments.graph.spec.js +++ b/packages/server/modules/comments/tests/comments.graph.spec.js @@ -10,7 +10,6 @@ const { } = require('@/modules/core/services/streams') const { createUser } = require('@/modules/core/services/users') const { gql } = require('graphql-tag') -const { createStream } = require('@/modules/core/services/streams') const { createObject } = require('@/modules/core/services/objects') const { convertBasicStringToDocument @@ -49,14 +48,47 @@ const { const { getBranchByIdFactory, markCommitBranchUpdatedFactory, - getStreamBranchByNameFactory + getStreamBranchByNameFactory, + createBranchFactory } = require('@/modules/core/repositories/branches') -const { markCommitStreamUpdated } = require('@/modules/core/repositories/streams') +const { + markCommitStreamUpdated, + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { addCommitCreatedActivity } = require('@/modules/activitystream/services/commitActivity') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') const streamResourceCheck = streamResourceCheckFactory({ checkStreamResourceAccess: checkStreamResourceAccessFactory({ db }) @@ -93,6 +125,38 @@ const createCommitByBranchName = createCommitByBranchNameFactory({ getBranchById: getBranchByIdFactory({ db }) }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + function buildCommentInputFromString(textString) { return convertBasicStringToDocument(textString) } diff --git a/packages/server/modules/comments/tests/comments.spec.js b/packages/server/modules/comments/tests/comments.spec.js index 9fc91e1c9..d76239744 100644 --- a/packages/server/modules/comments/tests/comments.spec.js +++ b/packages/server/modules/comments/tests/comments.spec.js @@ -4,7 +4,6 @@ const expect = require('chai').expect const crs = require('crypto-random-string') const { beforeEachContext, truncateTables } = require('@/test/hooks') const { createUser } = require('@/modules/core/services/users') -const { createStream } = require('@/modules/core/services/streams') const { createObject } = require('@/modules/core/services/objects') const { @@ -57,7 +56,8 @@ const { getBlobsFactory } = require('@/modules/blobstorage/repositories') const { CommentsEmitter } = require('@/modules/comments/events/emitter') const { markCommitStreamUpdated, - getStreamFactory + getStreamFactory, + createStreamFactory } = require('@/modules/core/repositories/streams') const { createCommitByBranchIdFactory, @@ -71,13 +71,42 @@ const { const { getBranchByIdFactory, markCommitBranchUpdatedFactory, - getStreamBranchByNameFactory + getStreamBranchByNameFactory, + createBranchFactory } = require('@/modules/core/repositories/branches') const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { addCommitCreatedActivity } = require('@/modules/activitystream/services/commitActivity') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') const getStream = getStreamFactory({ db }) const streamResourceCheck = streamResourceCheckFactory({ @@ -144,6 +173,37 @@ const createCommitByBranchName = createCommitByBranchNameFactory({ getBranchById: getBranchByIdFactory({ db }) }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + function buildCommentInputFromString(textString) { return convertBasicStringToDocument(textString) } diff --git a/packages/server/modules/core/domain/streams/operations.ts b/packages/server/modules/core/domain/streams/operations.ts index 6fd50654a..2238a857c 100644 --- a/packages/server/modules/core/domain/streams/operations.ts +++ b/packages/server/modules/core/domain/streams/operations.ts @@ -1,9 +1,15 @@ import { StreamWithCommitId, StreamWithOptionalRole, - LimitedUserWithStreamRole + LimitedUserWithStreamRole, + Stream } from '@/modules/core/domain/streams/types' -import { Optional, StreamRoles } from '@speckle/shared' +import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types' +import { + ProjectCreateInput, + StreamCreateInput +} from '@/modules/core/graph/generated/graphql' +import { MaybeNullOrUndefined, Optional, StreamRoles } from '@speckle/shared' import { Knex } from 'knex' export type GetStreams = ( @@ -38,3 +44,25 @@ export type GetStreamCollaborators = ( streamId: string, type?: StreamRoles ) => Promise> + +export type StoreStream = ( + input: StreamCreateInput | ProjectCreateInput, + options?: Partial<{ + ownerId: string + trx: Knex.Transaction + }> +) => Promise + +export type CreateStream = ( + params: (StreamCreateInput | ProjectCreateInput) & { + ownerId: string + ownerResourceAccessRules?: MaybeNullOrUndefined + }, + options?: Partial<{ + createActivity: boolean + }> +) => Promise + +export type LegacyCreateStream = ( + params: StreamCreateInput & { ownerId: string } +) => Promise diff --git a/packages/server/modules/core/events/projectsEmitter.ts b/packages/server/modules/core/events/projectsEmitter.ts index 89ecbfc86..01e21cf2e 100644 --- a/packages/server/modules/core/events/projectsEmitter.ts +++ b/packages/server/modules/core/events/projectsEmitter.ts @@ -17,3 +17,5 @@ const { emit, listen } = initializeModuleEventEmitter({ }) export const ProjectsEmitter = { emit, listen, events: ProjectEvents } +export type ProjectsEventsEmitter = (typeof ProjectsEmitter)['emit'] +export type ProjectsEventsListener = (typeof ProjectsEmitter)['listen'] diff --git a/packages/server/modules/core/graph/resolvers/projects.ts b/packages/server/modules/core/graph/resolvers/projects.ts index 0ff1b19d9..e026dd961 100644 --- a/packages/server/modules/core/graph/resolvers/projects.ts +++ b/packages/server/modules/core/graph/resolvers/projects.ts @@ -1,7 +1,10 @@ import { db } from '@/db/knex' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' import { RateLimitError } from '@/modules/core/errors/ratelimit' import { StreamNotFoundError } from '@/modules/core/errors/stream' import { WorkspacesModuleDisabledError } from '@/modules/core/errors/workspaces' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' import { ProjectVisibility, Resolvers, @@ -10,35 +13,75 @@ import { import { isWorkspacesModuleEnabled } from '@/modules/core/helpers/features' import { Roles, Scopes, StreamRoles } from '@/modules/core/helpers/mainConstants' import { isResourceAllowed, toProjectIdWhitelist } from '@/modules/core/helpers/token' +import { createBranchFactory } from '@/modules/core/repositories/branches' import { getUserStreamsCount, getUserStreams, getStreamFactory, - getStreamCollaboratorsFactory + getStreamCollaboratorsFactory, + createStreamFactory } from '@/modules/core/repositories/streams' +import { getUsers } from '@/modules/core/repositories/users' import { getRateLimitResult, isRateLimitBreached } from '@/modules/core/services/ratelimiter' import { - createStreamReturnRecord, + createStreamReturnRecordFactory, deleteStreamAndNotify, updateStreamAndNotify, updateStreamRoleAndNotify } from '@/modules/core/services/streams/management' import { createOnboardingStream } from '@/modules/core/services/streams/onboarding' import { removeStreamCollaborator } from '@/modules/core/services/streams/streamAccessService' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { authorizeResolver, validateScopes } from '@/modules/shared' import { throwForNotHavingServerRole } from '@/modules/shared/authz' +import { getEventBus } from '@/modules/shared/services/eventBus' import { filteredSubscribe, ProjectSubscriptions, + publish, UserSubscriptions } from '@/modules/shared/utils/subscriptions' import { has } from 'lodash' const getStream = getStreamFactory({ db }) const getStreamCollaborators = getStreamCollaboratorsFactory({ db }) +const createStreamReturnRecord = createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity: addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish + }), + projectsEventsEmitter: ProjectsEmitter.emit +}) export = { Query: { diff --git a/packages/server/modules/core/graph/resolvers/streams.ts b/packages/server/modules/core/graph/resolvers/streams.ts index e371993fa..1c14b2047 100644 --- a/packages/server/modules/core/graph/resolvers/streams.ts +++ b/packages/server/modules/core/graph/resolvers/streams.ts @@ -9,6 +9,7 @@ import { } from '@/modules/core/services/streams' import { filteredSubscribe, + publish, StreamSubscriptions } from '@/modules/shared/utils/subscriptions' import { authorizeResolver, validateScopes } from '@/modules/shared' @@ -16,7 +17,10 @@ import { getRateLimitResult, isRateLimitBreached } from '@/modules/core/services/ratelimiter' -import { getPendingProjectCollaboratorsFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { + getPendingProjectCollaboratorsFactory, + inviteUsersToProjectFactory +} from '@/modules/serverinvites/services/projectInviteManagement' import { removePrivateFields } from '@/modules/core/helpers/userHelper' import { removeStreamCollaborator } from '@/modules/core/services/streams/streamAccessService' import { getDiscoverableStreams } from '@/modules/core/services/streams/discoverableStreams' @@ -24,12 +28,13 @@ import { get } from 'lodash' import { getUserStreamsCount, getUserStreams, - getStreamFactory + getStreamFactory, + createStreamFactory } from '@/modules/core/repositories/streams' import { + createStreamReturnRecordFactory, deleteStreamAndNotify, updateStreamAndNotify, - createStreamReturnRecord, updateStreamRoleAndNotify } from '@/modules/core/services/streams/management' import { adminOverrideEnabled } from '@/modules/shared/helpers/envHelper' @@ -44,13 +49,52 @@ import { TokenResourceIdentifierType, UserStreamsArgs } from '@/modules/core/graph/generated/graphql' -import { queryAllResourceInvitesFactory } from '@/modules/serverinvites/repositories/serverInvites' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory, + queryAllResourceInvitesFactory +} from '@/modules/serverinvites/repositories/serverInvites' import db from '@/db/knex' import { getInvitationTargetUsersFactory } from '@/modules/serverinvites/services/retrieval' import { getUsers } from '@/modules/core/repositories/users' import { BadRequestError } from '@/modules/shared/errors' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { createBranchFactory } from '@/modules/core/repositories/branches' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' const getStream = getStreamFactory({ db }) +const createStreamReturnRecord = createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity: addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish + }), + projectsEventsEmitter: ProjectsEmitter.emit +}) const getUserStreamsCore = async ( forOtherUser: boolean, diff --git a/packages/server/modules/core/repositories/streams.ts b/packages/server/modules/core/repositories/streams.ts index a123f8ee4..864415b5c 100644 --- a/packages/server/modules/core/repositories/streams.ts +++ b/packages/server/modules/core/repositories/streams.ts @@ -72,6 +72,7 @@ import { StreamWithOptionalRole } from '@/modules/core/domain/streams/types' import { + StoreStream, GetCommitStream, GetCommitStreams, GetStream, @@ -815,63 +816,67 @@ export async function getUserStreamsCount(params: UserStreamsQueryCountParams) { return parseInt(res.count) } -export async function createStream( - input: StreamCreateInput | ProjectCreateInput, - options?: Partial<{ - /** - * If set, will assign owner permissions to this user - */ - ownerId: string - trx: Knex.Transaction - }> -) { - const { name, description } = input - const { ownerId, trx } = options || {} +export const createStreamFactory = + (deps: { db: Knex }): StoreStream => + async ( + input: StreamCreateInput | ProjectCreateInput, + options?: Partial<{ + /** + * If set, will assign owner permissions to this user + */ + ownerId: string + trx: Knex.Transaction + }> + ) => { + const { name, description } = input + const { ownerId, trx } = options || {} - let shouldBePublic: boolean, shouldBeDiscoverable: boolean - if (isProjectCreateInput(input)) { - shouldBeDiscoverable = input.visibility === ProjectVisibility.Public - shouldBePublic = - !input.visibility || - [ProjectVisibility.Public, ProjectVisibility.Unlisted].includes(input.visibility) - } else { - shouldBePublic = input.isPublic !== false - shouldBeDiscoverable = input.isDiscoverable !== false && shouldBePublic + let shouldBePublic: boolean, shouldBeDiscoverable: boolean + if (isProjectCreateInput(input)) { + shouldBeDiscoverable = input.visibility === ProjectVisibility.Public + shouldBePublic = + !input.visibility || + [ProjectVisibility.Public, ProjectVisibility.Unlisted].includes( + input.visibility + ) + } else { + shouldBePublic = input.isPublic !== false + shouldBeDiscoverable = input.isDiscoverable !== false && shouldBePublic + } + + const workspaceId = 'workspaceId' in input ? input.workspaceId : null + + const id = generateId() + const stream = { + id, + name: name || generateStreamName(), + description: description || '', + isPublic: shouldBePublic, + isDiscoverable: shouldBeDiscoverable, + updatedAt: knex.fn.now(), + workspaceId: workspaceId || null + } + + // Create the stream & set up permissions + const streamQuery = tables.streams(deps.db).insert(stream, '*') + if (trx) streamQuery.transacting(trx) + + const insertResults = await streamQuery + const newStream = insertResults[0] as StreamRecord + + if (ownerId) { + const streamAclQuery = tables.streamAcl(deps.db).insert({ + userId: ownerId, + resourceId: id, + role: Roles.Stream.Owner + }) + if (trx) streamAclQuery.transacting(trx) + await streamAclQuery + } + + return newStream } - const workspaceId = 'workspaceId' in input ? input.workspaceId : null - - const id = generateId() - const stream = { - id, - name: name || generateStreamName(), - description: description || '', - isPublic: shouldBePublic, - isDiscoverable: shouldBeDiscoverable, - updatedAt: knex.fn.now(), - workspaceId: workspaceId || null - } - - // Create the stream & set up permissions - const streamQuery = Streams.knex().insert(stream, '*') - if (trx) streamQuery.transacting(trx) - - const insertResults = await streamQuery - const newStream = insertResults[0] as StreamRecord - - if (ownerId) { - const streamAclQuery = StreamAcl.knex().insert({ - userId: ownerId, - resourceId: id, - role: Roles.Stream.Owner - }) - if (trx) streamAclQuery.transacting(trx) - await streamAclQuery - } - - return newStream -} - export async function getUserStreamCounts(params: { userIds: string[] /** diff --git a/packages/server/modules/core/services/streams.js b/packages/server/modules/core/services/streams.js index 3e9a98681..bd0b145ef 100644 --- a/packages/server/modules/core/services/streams.js +++ b/packages/server/modules/core/services/streams.js @@ -14,9 +14,6 @@ const { } = require('@/modules/core/repositories/streams') const { UnauthorizedError, InvalidArgumentError } = require('@/modules/shared/errors') const { dbLogger } = require('@/logging/logging') -const { - createStreamReturnRecord -} = require('@/modules/core/services/streams/management') const { isResourceAllowed } = require('@/modules/core/helpers/token') const { TokenResourceIdentifierType @@ -30,18 +27,6 @@ const { */ module.exports = { - /** - * @deprecated Use createStreamReturnRecord() - * @param {import('@/modules/core/graph/generated/graphql').StreamCreateInput & {ownerId: string}} param0 - * @returns {Promise} - */ - async createStream(params) { - const { id } = await createStreamReturnRecord(params, { - createActivity: false - }) - return id - }, - /** * @deprecated Use updateStreamAndNotify or use the repository function directly * @param {import('@/modules/core/graph/generated/graphql').StreamUpdateInput} update diff --git a/packages/server/modules/core/services/streams/clone.ts b/packages/server/modules/core/services/streams/clone.ts index a5ca3a4cc..4176acc68 100644 --- a/packages/server/modules/core/services/streams/clone.ts +++ b/packages/server/modules/core/services/streams/clone.ts @@ -5,7 +5,7 @@ import { UserRecord } from '@/modules/core/helpers/types' import { - createStream, + createStreamFactory, getStreamFactory, StreamWithOptionalRole } from '@/modules/core/repositories/streams' @@ -103,6 +103,7 @@ const prepareState = async ( async function cloneStreamEntity(state: CloneStreamInitialState) { const { targetStream, user, trx } = state + const createStream = createStreamFactory({ db }) const newStream = await createStream( { name: targetStream.name, diff --git a/packages/server/modules/core/services/streams/management.ts b/packages/server/modules/core/services/streams/management.ts index f8889b0dd..7448ff6b7 100644 --- a/packages/server/modules/core/services/streams/management.ts +++ b/packages/server/modules/core/services/streams/management.ts @@ -15,12 +15,10 @@ import { } from '@/modules/core/graph/generated/graphql' import { StreamRecord } from '@/modules/core/helpers/types' import { - createStream, deleteStream, getStreamFactory, updateStream } from '@/modules/core/repositories/streams' -import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' import { StreamInvalidAccessError, StreamUpdateError @@ -37,99 +35,104 @@ import { isNewResourceAllowed } from '@/modules/core/helpers/token' import { authorizeResolver } from '@/modules/shared' -import { - deleteAllResourceInvitesFactory, - findUserByTargetFactory, - insertInviteAndDeleteOldFactory -} from '@/modules/serverinvites/repositories/serverInvites' +import { deleteAllResourceInvitesFactory } from '@/modules/serverinvites/repositories/serverInvites' import db from '@/db/knex' import { TokenResourceIdentifier, TokenResourceIdentifierType } from '@/modules/core/domain/tokens/types' -import { ProjectEvents, ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { + ProjectEvents, + ProjectsEventsEmitter +} from '@/modules/core/events/projectsEmitter' import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' -import { getUsers } from '@/modules/core/repositories/users' -import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' -import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' -import { getEventBus } from '@/modules/shared/services/eventBus' import { ProjectInviteResourceType } from '@/modules/serverinvites/domain/constants' -import { createBranchFactory } from '@/modules/core/repositories/branches' -import { saveActivityFactory } from '@/modules/activitystream/repositories' -import { publish } from '@/modules/shared/utils/subscriptions' +import { + CreateStream, + LegacyCreateStream, + StoreStream +} from '@/modules/core/domain/streams/operations' +import { StoreBranch } from '@/modules/core/domain/branches/operations' -export async function createStreamReturnRecord( - params: (StreamCreateInput | ProjectCreateInput) & { - ownerId: string - ownerResourceAccessRules?: MaybeNullOrUndefined - }, - options?: Partial<{ createActivity: boolean }> -): Promise { - const { ownerId, ownerResourceAccessRules } = params - const { createActivity = true } = options || {} +export const createStreamReturnRecordFactory = + (deps: { + createStream: StoreStream + createBranch: StoreBranch + inviteUsersToProject: ReturnType + addStreamCreatedActivity: ReturnType + projectsEventsEmitter: ProjectsEventsEmitter + }): CreateStream => + async ( + params: (StreamCreateInput | ProjectCreateInput) & { + ownerId: string + ownerResourceAccessRules?: MaybeNullOrUndefined + }, + options?: Partial<{ createActivity: boolean }> + ): Promise => { + const { ownerId, ownerResourceAccessRules } = params + const { createActivity = true } = options || {} - const canCreateStream = isNewResourceAllowed({ - resourceType: TokenResourceIdentifierType.Project, - resourceAccessRules: ownerResourceAccessRules - }) - if (!canCreateStream) { - throw new StreamInvalidAccessError( - 'You do not have the permissions to create a new stream' - ) - } - - const stream = await createStream(params, { ownerId }) - const streamId = stream.id - - // Create a default main branch - await createBranchFactory({ db })({ - name: 'main', - description: 'default branch', - streamId, - authorId: ownerId - }) - - // Invite contributors? - if (!isProjectCreateInput(params) && params.withContributors?.length) { - // TODO: should be injected in the resolver - const getStream = getStreamFactory({ db }) - await inviteUsersToProjectFactory({ - createAndSendInvite: createAndSendInviteFactory({ - findUserByTarget: findUserByTargetFactory(), - insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), - collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ - getStream - }), - buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ - getStream - }), - emitEvent: ({ eventName, payload }) => - getEventBus().emit({ - eventName, - payload - }) - }), - getUsers - })(ownerId, streamId, params.withContributors, ownerResourceAccessRules) - } - - // Save activity - if (createActivity) { - await addStreamCreatedActivityFactory({ - saveActivity: saveActivityFactory({ db }), - publish - })({ - streamId, - input: params, - stream, - creatorId: ownerId + const canCreateStream = isNewResourceAllowed({ + resourceType: TokenResourceIdentifierType.Project, + resourceAccessRules: ownerResourceAccessRules }) + if (!canCreateStream) { + throw new StreamInvalidAccessError( + 'You do not have the permissions to create a new stream' + ) + } + + const stream = await deps.createStream(params, { ownerId }) + const streamId = stream.id + + // Create a default main branch + await deps.createBranch({ + name: 'main', + description: 'default branch', + streamId, + authorId: ownerId + }) + + // Invite contributors? + if (!isProjectCreateInput(params) && params.withContributors?.length) { + // TODO: should be injected in the resolver + await deps.inviteUsersToProject( + ownerId, + streamId, + params.withContributors, + ownerResourceAccessRules + ) + } + + // Save activity + if (createActivity) { + await deps.addStreamCreatedActivity({ + streamId, + input: params, + stream, + creatorId: ownerId + }) + } + + await deps.projectsEventsEmitter(ProjectEvents.Created, { + project: stream, + ownerId + }) + + return stream } - await ProjectsEmitter.emit(ProjectEvents.Created, { project: stream, ownerId }) - - return stream -} +/** + * @deprecated Use createStreamReturnRecordFactory() + */ +export const legacyCreateStreamFactory = + (deps: { createStreamReturnRecord: CreateStream }): LegacyCreateStream => + async (params) => { + const { id } = await deps.createStreamReturnRecord(params, { + createActivity: false + }) + return id + } /** * Delete stream & notify users (emit events & save activity) diff --git a/packages/server/modules/core/services/streams/onboarding.ts b/packages/server/modules/core/services/streams/onboarding.ts index a71ae917d..cc36386fb 100644 --- a/packages/server/modules/core/services/streams/onboarding.ts +++ b/packages/server/modules/core/services/streams/onboarding.ts @@ -6,10 +6,11 @@ import { import { cloneStream } from '@/modules/core/services/streams/clone' import { StreamRecord } from '@/modules/core/helpers/types' import { logger } from '@/logging/logging' -import { createStreamReturnRecord } from '@/modules/core/services/streams/management' import { getOnboardingBaseProjectFactory } from '@/modules/cross-server-sync/services/onboardingProject' import { + createStreamFactory, getOnboardingBaseStream, + getStreamFactory, updateStream } from '@/modules/core/repositories/streams' import { getUser } from '@/modules/core/services/users' @@ -18,6 +19,23 @@ import { isNewResourceAllowed } from '@/modules/core/helpers/token' import { TokenResourceIdentifierType } from '@/modules/core/graph/generated/graphql' +import { createStreamReturnRecordFactory } from '@/modules/core/services/streams/management' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { db } from '@/db/knex' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { getUsers } from '@/modules/core/repositories/users' +import { createBranchFactory } from '@/modules/core/repositories/branches' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' export async function createOnboardingStream( targetUserId: string, @@ -33,6 +51,36 @@ export async function createOnboardingStream( ) } + const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish + }) + const getStream = getStreamFactory({ db }) + const createStreamReturnRecord = createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) + const sourceStream = await getOnboardingBaseProjectFactory({ getOnboardingBaseStream })() diff --git a/packages/server/modules/core/tests/branches.spec.js b/packages/server/modules/core/tests/branches.spec.js index c68bbe0c9..867b4f98d 100644 --- a/packages/server/modules/core/tests/branches.spec.js +++ b/packages/server/modules/core/tests/branches.spec.js @@ -9,7 +9,6 @@ const expect = chai.expect const knex = require('@/db/knex') const { createUser } = require('../services/users') -const { createStream } = require('../services/streams') const { createObject } = require('../services/objects') const { getBranchesByStreamId } = require('../services/branches') @@ -32,7 +31,8 @@ const { const { markBranchStreamUpdated, markCommitStreamUpdated, - getStreamFactory + getStreamFactory, + createStreamFactory } = require('@/modules/core/repositories/streams') const { ModelsEmitter } = require('@/modules/core/events/modelsEmitter') const { @@ -49,6 +49,34 @@ const { addCommitCreatedActivity } = require('@/modules/activitystream/services/commitActivity') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') const db = knex const Commits = () => knex('commits') @@ -89,6 +117,37 @@ const createCommitByBranchName = createCommitByBranchNameFactory({ getBranchById: getBranchByIdFactory({ db }) }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + describe('Branches @core-branches', () => { const user = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/commits.spec.js b/packages/server/modules/core/tests/commits.spec.js index bc5a0f20e..998a9c277 100644 --- a/packages/server/modules/core/tests/commits.spec.js +++ b/packages/server/modules/core/tests/commits.spec.js @@ -4,7 +4,6 @@ const expect = require('chai').expect const { beforeEachContext } = require('@/test/hooks') const { createUser } = require('../services/users') -const { createStream } = require('../services/streams') const { createObject } = require('../services/objects') const { @@ -47,7 +46,8 @@ const { const { markCommitStreamUpdated, getStreamFactory, - getCommitStreamFactory + getCommitStreamFactory, + createStreamFactory } = require('@/modules/core/repositories/streams') const { addCommitDeletedActivity, @@ -56,6 +56,34 @@ const { } = require('@/modules/activitystream/services/commitActivity') const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') const getCommitStream = getCommitStreamFactory({ db }) const getStream = getStreamFactory({ db }) @@ -107,6 +135,37 @@ const updateCommitAndNotify = updateCommitAndNotifyFactory({ }) const getStreamCommitCount = getStreamCommitCountFactory({ db }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + describe('Commits @core-commits', () => { const user = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/favoriteStreams.spec.js b/packages/server/modules/core/tests/favoriteStreams.spec.js index 5780cb84d..909eb3c89 100644 --- a/packages/server/modules/core/tests/favoriteStreams.spec.js +++ b/packages/server/modules/core/tests/favoriteStreams.spec.js @@ -3,7 +3,6 @@ const expect = require('chai').expect const { buildApolloServer } = require('@/app') const { StreamFavorites, Streams, Users } = require('@/modules/core/dbSchema') -const { createStream } = require('@/modules/core/services/streams') const { createUser } = require('@/modules/core/services/users') const { truncateTables } = require('@/test/hooks') const { gql } = require('graphql-tag') @@ -13,6 +12,72 @@ const { createTestContext, executeOperation } = require('@/test/graphqlHelper') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') + +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) /** * Cleaning up relevant tables diff --git a/packages/server/modules/core/tests/generic.spec.js b/packages/server/modules/core/tests/generic.spec.js index cce282b0e..72d799d3c 100644 --- a/packages/server/modules/core/tests/generic.spec.js +++ b/packages/server/modules/core/tests/generic.spec.js @@ -10,7 +10,6 @@ const envHelperMock = mockRequireModule( const expect = require('chai').expect const { beforeEachContext } = require('@/test/hooks') -const { createStream } = require('@/modules/core/services/streams') const { createUser } = require('@/modules/core/services/users') const { validateScopes, authorizeResolver } = require('@/modules/shared') @@ -18,6 +17,72 @@ const { buildContext } = require('@/modules/shared/middleware') const { Roles, Scopes } = require('@speckle/shared') const { throwForNotHavingServerRole } = require('@/modules/shared/authz') const { ForbiddenError } = require('@/modules/shared/errors') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') + +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) describe('Generic AuthN & AuthZ controller tests', () => { before(async () => { diff --git a/packages/server/modules/core/tests/objects.spec.js b/packages/server/modules/core/tests/objects.spec.js index c663a873b..320c86527 100644 --- a/packages/server/modules/core/tests/objects.spec.js +++ b/packages/server/modules/core/tests/objects.spec.js @@ -8,7 +8,6 @@ const { beforeEachContext } = require('@/test/hooks') const { getAnIdForThisOnePlease } = require('@/test/helpers') const { createUser } = require('../services/users') -const { createStream } = require('../services/streams') const { createObject, createObjects, @@ -19,6 +18,40 @@ const { getObjectChildrenQuery, getObjectChildrenStream } = require('../services/objects') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') const sampleCommit = JSON.parse(`{ "Objects": [ @@ -43,6 +76,38 @@ const sampleObject = JSON.parse(`{ "speckleType": "Tests.Polyline" }`) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + describe('Objects @core-objects', () => { const userOne = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/rest.spec.js b/packages/server/modules/core/tests/rest.spec.js index e57d44cd2..fb881dc27 100644 --- a/packages/server/modules/core/tests/rest.spec.js +++ b/packages/server/modules/core/tests/rest.spec.js @@ -10,8 +10,73 @@ const { createManyObjects } = require('@/test/helpers') const { createUser } = require('../services/users') const { createPersonalAccessToken } = require('../services/tokens') -const { createStream } = require('../services/streams') const { Scopes } = require('@speckle/shared') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') + +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) describe('Upload/Download Routes @api-rest', () => { const userA = { diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index f4a7e89f0..34f516f2f 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -1,6 +1,5 @@ import { expect } from 'chai' import { - createStream, updateStream, deleteStream, getStreamUsers, @@ -28,6 +27,7 @@ import { } from '@/test/speckle-helpers/streamHelper' import { StreamWithOptionalRole, + createStreamFactory, getStreamFactory, markBranchStreamUpdated, markCommitStreamUpdated, @@ -73,6 +73,24 @@ import { import { VersionsEmitter } from '@/modules/core/events/versionsEmitter' import { addCommitCreatedActivity } from '@/modules/activitystream/services/commitActivity' import { getObjectFactory } from '@/modules/core/repositories/objects' +import { + createStreamReturnRecordFactory, + legacyCreateStreamFactory +} from '@/modules/core/services/streams/management' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { getUsers } from '@/modules/core/repositories/users' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' const getStream = getStreamFactory({ db }) const getStreamBranchByName = getStreamBranchByNameFactory({ db }) @@ -105,6 +123,37 @@ const createCommitByBranchName = createCommitByBranchNameFactory({ getBranchById: getBranchByIdFactory({ db }) }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + describe('Streams @core-streams', () => { const userOne: BasicTestUser = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/users.spec.js b/packages/server/modules/core/tests/users.spec.js index 98ee10d2d..e1d2f150f 100644 --- a/packages/server/modules/core/tests/users.spec.js +++ b/packages/server/modules/core/tests/users.spec.js @@ -21,7 +21,7 @@ const { validateToken, getUserTokens } = require('../services/tokens') -const { grantPermissionsStream, createStream } = require('../services/streams') +const { grantPermissionsStream } = require('../services/streams') const { getBranchesByStreamId } = require('../services/branches') @@ -50,13 +50,42 @@ const { } = require('@/modules/core/services/commit/management') const { markCommitStreamUpdated, - getStreamFactory + getStreamFactory, + createStreamFactory } = require('@/modules/core/repositories/streams') const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { addCommitCreatedActivity } = require('@/modules/activitystream/services/commitActivity') const { getObjectFactory } = require('@/modules/core/repositories/objects') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') const getStream = getStreamFactory({ db }) const createBranch = createBranchFactory({ db }) @@ -81,6 +110,37 @@ const createCommitByBranchName = createCommitByBranchNameFactory({ getBranchById: getBranchByIdFactory({ db }) }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + describe('Actors & Tokens @user-services', () => { const myTestActor = { name: 'Dimitrie Stefanescu', diff --git a/packages/server/modules/core/tests/usersAdminList.spec.ts b/packages/server/modules/core/tests/usersAdminList.spec.ts index 3119416ef..77533b997 100644 --- a/packages/server/modules/core/tests/usersAdminList.spec.ts +++ b/packages/server/modules/core/tests/usersAdminList.spec.ts @@ -1,7 +1,6 @@ import { ServerInvites, Streams, Users } from '@/modules/core/dbSchema' import { truncateTables } from '@/test/hooks' import { createUser } from '@/modules/core/services/users' -import { createStream } from '@/modules/core/services/streams' import { times, clamp } from 'lodash' import { createStreamInviteDirectly } from '@/test/speckle-helpers/inviteHelper' import { getAdminUsersList } from '@/test/graphql/users' @@ -11,10 +10,65 @@ import { expect } from 'chai' import { Optional } from '@/modules/shared/helpers/typeHelper' import { wait } from '@speckle/shared' import { createAuthedTestContext, ServerAndContext } from '@/test/graphqlHelper' +import { + createStreamFactory, + getStreamFactory +} from '@/modules/core/repositories/streams' +import { db } from '@/db/knex' +import { + createStreamReturnRecordFactory, + legacyCreateStreamFactory +} from '@/modules/core/services/streams/management' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { getUsers } from '@/modules/core/repositories/users' +import { createBranchFactory } from '@/modules/core/repositories/branches' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' // To ensure that the invites are created in the correct order, we need to wait a bit between each creation const WAIT_TIMEOUT = 5 +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) const createInviteDirectly = createStreamInviteDirectly function randomEl(array: T[]): T { diff --git a/packages/server/modules/cross-server-sync/index.ts b/packages/server/modules/cross-server-sync/index.ts index 4bd195b59..be96d47fa 100644 --- a/packages/server/modules/cross-server-sync/index.ts +++ b/packages/server/modules/cross-server-sync/index.ts @@ -1,11 +1,13 @@ import { db } from '@/db/knex' import { moduleLogger, crossServerSyncLogger } from '@/logging/logging' +import { saveActivityFactory } from '@/modules/activitystream/repositories' import { addBranchCreatedActivity } from '@/modules/activitystream/services/branchActivity' import { addCommentCreatedActivity, addReplyAddedActivity } from '@/modules/activitystream/services/commentActivity' import { addCommitCreatedActivity } from '@/modules/activitystream/services/commitActivity' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' import { getBlobsFactory } from '@/modules/blobstorage/repositories' import { CommentsEmitter } from '@/modules/comments/events/emitter' import { @@ -20,6 +22,7 @@ import { createCommentReplyAndNotifyFactory, createCommentThreadAndNotifyFactory } from '@/modules/comments/services/management' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' import { VersionsEmitter } from '@/modules/core/events/versionsEmitter' import { createBranchFactory, @@ -41,13 +44,14 @@ import { getStreamObjectsFactory } from '@/modules/core/repositories/objects' import { + createStreamFactory, getOnboardingBaseStream, getStreamCollaboratorsFactory, getStreamFactory, markCommitStreamUpdated, markOnboardingBaseStream } from '@/modules/core/repositories/streams' -import { getFirstAdmin, getUser } from '@/modules/core/repositories/users' +import { getFirstAdmin, getUser, getUsers } from '@/modules/core/repositories/users' import { createBranchAndNotifyFactory } from '@/modules/core/services/branch/management' import { createCommitByBranchIdFactory } from '@/modules/core/services/commit/management' import { @@ -55,11 +59,21 @@ import { getViewerResourceItemsUngroupedFactory } from '@/modules/core/services/commit/viewerResources' import { createObject } from '@/modules/core/services/objects' -import { createStreamReturnRecord } from '@/modules/core/services/streams/management' +import { createStreamReturnRecordFactory } from '@/modules/core/services/streams/management' import { downloadCommitFactory } from '@/modules/cross-server-sync/services/commit' import { ensureOnboardingProjectFactory } from '@/modules/cross-server-sync/services/onboardingProject' import { downloadProjectFactory } from '@/modules/cross-server-sync/services/project' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { SpeckleModule } from '@/modules/shared/helpers/typeHelper' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { publish } from '@/modules/shared/utils/subscriptions' const crossServerSyncModule: SpeckleModule = { init() { @@ -117,6 +131,33 @@ const crossServerSyncModule: SpeckleModule = { addCommitCreatedActivity }) + const createStreamReturnRecord = createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity: addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish + }), + projectsEventsEmitter: ProjectsEmitter.emit + }) const ensureOnboardingProject = ensureOnboardingProjectFactory({ getOnboardingBaseStream, getFirstAdmin, diff --git a/packages/server/modules/cross-server-sync/services/project.ts b/packages/server/modules/cross-server-sync/services/project.ts index bdbd7b91f..a28cdc929 100644 --- a/packages/server/modules/cross-server-sync/services/project.ts +++ b/packages/server/modules/cross-server-sync/services/project.ts @@ -10,7 +10,6 @@ import { import { CrossSyncProjectMetadataQuery } from '@/modules/cross-server-sync/graph/generated/graphql' import { omit } from 'lodash' import { getFrontendOrigin } from '@/modules/shared/helpers/envHelper' -import { createStreamReturnRecord } from '@/modules/core/services/streams/management' import { DownloadCommit, DownloadProject @@ -19,6 +18,7 @@ import { CreateBranchAndNotify, GetStreamBranchByName } from '@/modules/core/domain/branches/operations' +import { CreateStream } from '@/modules/core/domain/streams/operations' type ProjectMetadata = Awaited> @@ -193,7 +193,7 @@ const importVersionsFactory = } type DownloadProjectDeps = { - createStreamReturnRecord: typeof createStreamReturnRecord + createStreamReturnRecord: CreateStream } & GetLocalResourcesDeps & ImportVersionsDeps diff --git a/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts b/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts index 9f73fb438..41948a74d 100644 --- a/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts +++ b/packages/server/modules/fileuploads/tests/fileuploads.integration.spec.ts @@ -4,7 +4,6 @@ import { expect } from 'chai' import { beforeEachContext, initializeTestServer } from '@/test/hooks' import { createUser } from '@/modules/core/services/users' -import { createStream } from '@/modules/core/services/streams' import { createToken } from '@/modules/core/services/tokens' import type { Server } from 'http' import type { Express } from 'express' @@ -12,6 +11,62 @@ import request from 'supertest' import { Scopes } from '@/modules/core/helpers/mainConstants' import cryptoRandomString from 'crypto-random-string' import { noErrors } from '@/test/helpers' +import { + createStreamFactory, + getStreamFactory +} from '@/modules/core/repositories/streams' +import { db } from '@/db/knex' +import { + createStreamReturnRecordFactory, + legacyCreateStreamFactory +} from '@/modules/core/services/streams/management' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { getUsers } from '@/modules/core/repositories/users' +import { createBranchFactory } from '@/modules/core/repositories/branches' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' + +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) describe('FileUploads @fileuploads', () => { let server: Server diff --git a/packages/server/modules/stats/tests/stats.spec.ts b/packages/server/modules/stats/tests/stats.spec.ts index 6ba34b13b..56c56bfd3 100644 --- a/packages/server/modules/stats/tests/stats.spec.ts +++ b/packages/server/modules/stats/tests/stats.spec.ts @@ -2,7 +2,6 @@ import { expect } from 'chai' import { createUser } from '@/modules/core/services/users' import { createPersonalAccessToken } from '@/modules/core/services/tokens' -import { createStream } from '@/modules/core/services/streams' import { createObjects } from '@/modules/core/services/objects' import { beforeEachContext, initializeTestServer } from '@/test/hooks' import { createManyObjects } from '@/test/helpers' @@ -31,14 +30,37 @@ import { insertStreamCommitsFactory } from '@/modules/core/repositories/commits' import { + createBranchFactory, getBranchByIdFactory, getStreamBranchByNameFactory, markCommitBranchUpdatedFactory } from '@/modules/core/repositories/branches' -import { markCommitStreamUpdated } from '@/modules/core/repositories/streams' +import { + createStreamFactory, + getStreamFactory, + markCommitStreamUpdated +} from '@/modules/core/repositories/streams' import { VersionsEmitter } from '@/modules/core/events/versionsEmitter' import { addCommitCreatedActivity } from '@/modules/activitystream/services/commitActivity' import { getObjectFactory } from '@/modules/core/repositories/objects' +import { + createStreamReturnRecordFactory, + legacyCreateStreamFactory +} from '@/modules/core/services/streams/management' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { getUsers } from '@/modules/core/repositories/users' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { publish } from '@/modules/shared/utils/subscriptions' const getObject = getObjectFactory({ db }) const createCommitByBranchId = createCommitByBranchIdFactory({ @@ -59,6 +81,38 @@ const createCommitByBranchName = createCommitByBranchNameFactory({ getBranchById: getBranchByIdFactory({ db }) }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + const params = { numUsers: 25, numStreams: 30, numObjects: 100, numCommits: 100 } describe('Server stats services @stats-services', function () { diff --git a/packages/server/modules/webhooks/tests/cleanup.spec.ts b/packages/server/modules/webhooks/tests/cleanup.spec.ts index e9521484a..e691cab8c 100644 --- a/packages/server/modules/webhooks/tests/cleanup.spec.ts +++ b/packages/server/modules/webhooks/tests/cleanup.spec.ts @@ -1,10 +1,32 @@ -import knex from '@/db/knex' +import knex, { db } from '@/db/knex' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' import { createRandomEmail, createRandomPassword } from '@/modules/core/helpers/testHelpers' -import { createStream } from '@/modules/core/services/streams' +import { createBranchFactory } from '@/modules/core/repositories/branches' +import { + createStreamFactory, + getStreamFactory +} from '@/modules/core/repositories/streams' +import { getUsers } from '@/modules/core/repositories/users' +import { + createStreamReturnRecordFactory, + legacyCreateStreamFactory +} from '@/modules/core/services/streams/management' import { createUser } from '@/modules/core/services/users' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { publish } from '@/modules/shared/utils/subscriptions' import { cleanOrphanedWebhookConfigs } from '@/modules/webhooks/services/cleanup' import { truncateTables } from '@/test/hooks' import { expect } from 'chai' @@ -16,6 +38,38 @@ const WEBHOOKS_EVENTS_TABLE = 'webhooks_events' const WebhooksConfig = () => knex(WEBHOOKS_CONFIG_TABLE) const randomId = () => crs({ length: 10 }) +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + const countWebhooks = async () => { const [{ count }] = await WebhooksConfig().count() return parseInt(count as string) diff --git a/packages/server/modules/webhooks/tests/webhooks.spec.js b/packages/server/modules/webhooks/tests/webhooks.spec.js index 7674e6380..b7919e239 100644 --- a/packages/server/modules/webhooks/tests/webhooks.spec.js +++ b/packages/server/modules/webhooks/tests/webhooks.spec.js @@ -10,7 +10,7 @@ const { const { noErrors } = require('@/test/helpers') const { createPersonalAccessToken } = require('../../core/services/tokens') const { createUser } = require('../../core/services/users') -const { createStream, grantPermissionsStream } = require('../../core/services/streams') +const { grantPermissionsStream } = require('../../core/services/streams') const { Scopes, Roles } = require('@speckle/shared') const { createWebhookConfigFactory, @@ -31,14 +31,75 @@ const { } = require('@/modules/webhooks/services/webhooks') const { Users, Streams } = require('@/modules/core/dbSchema') const { getServerInfo } = require('@/modules/core/services/generic') -const { getUser } = require('@/modules/core/repositories/users') -const { getStreamFactory } = require('@/modules/core/repositories/streams') +const { getUser, getUsers } = require('@/modules/core/repositories/users') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) const getStream = getStreamFactory({ db }) const updateWebhook = updateWebhookFactory({ updateWebhookConfig: updateWebhookConfigFactory({ db }) }) const getStreamWebhooks = getStreamWebhooksFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) describe('Webhooks @webhooks', () => { const getWebhook = getWebhookByIdFactory({ db }) diff --git a/packages/server/scripts/streamObjects.js b/packages/server/scripts/streamObjects.js index 5a4b1e254..4a104577f 100644 --- a/packages/server/scripts/streamObjects.js +++ b/packages/server/scripts/streamObjects.js @@ -1,7 +1,6 @@ require('../bootstrap') const { getUserByEmail } = require('@/modules/core/services/users') const { createPersonalAccessToken } = require('@/modules/core/services/tokens') -const { createStream } = require('@/modules/core/services/streams') const { createManyObjects } = require('@/test/helpers') const { fetch } = require('undici') @@ -10,6 +9,72 @@ const request = require('supertest') const { exit } = require('yargs') const { logger } = require('@/logging/logging') const { Scopes } = require('@speckle/shared') +const { + getStreamFactory, + createStreamFactory +} = require('@/modules/core/repositories/streams') +const { db } = require('@/db/knex') +const { + legacyCreateStreamFactory, + createStreamReturnRecordFactory +} = require('@/modules/core/services/streams/management') +const { + inviteUsersToProjectFactory +} = require('@/modules/serverinvites/services/projectInviteManagement') +const { + createAndSendInviteFactory +} = require('@/modules/serverinvites/services/creation') +const { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} = require('@/modules/serverinvites/repositories/serverInvites') +const { + collectAndValidateCoreTargetsFactory +} = require('@/modules/serverinvites/services/coreResourceCollection') +const { + buildCoreInviteEmailContentsFactory +} = require('@/modules/serverinvites/services/coreEmailContents') +const { getEventBus } = require('@/modules/shared/services/eventBus') +const { getUsers } = require('@/modules/core/repositories/users') +const { createBranchFactory } = require('@/modules/core/repositories/branches') +const { ProjectsEmitter } = require('@/modules/core/events/projectsEmitter') +const { + addStreamCreatedActivityFactory +} = require('@/modules/activitystream/services/streamActivity') +const { saveActivityFactory } = require('@/modules/activitystream/repositories') +const { publish } = require('@/modules/shared/utils/subscriptions') + +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) const main = async () => { const testStream = { diff --git a/packages/server/test/speckle-helpers/streamHelper.ts b/packages/server/test/speckle-helpers/streamHelper.ts index 503d87bf7..a21eca173 100644 --- a/packages/server/test/speckle-helpers/streamHelper.ts +++ b/packages/server/test/speckle-helpers/streamHelper.ts @@ -1,12 +1,67 @@ +import { db } from '@/db/knex' +import { saveActivityFactory } from '@/modules/activitystream/repositories' +import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' import { StreamAcl } from '@/modules/core/dbSchema' +import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' import { StreamAclRecord, StreamRecord } from '@/modules/core/helpers/types' -import { createStream } from '@/modules/core/services/streams' +import { createBranchFactory } from '@/modules/core/repositories/branches' +import { + createStreamFactory, + getStreamFactory +} from '@/modules/core/repositories/streams' +import { getUsers } from '@/modules/core/repositories/users' +import { + createStreamReturnRecordFactory, + legacyCreateStreamFactory +} from '@/modules/core/services/streams/management' import { removeStreamCollaborator } from '@/modules/core/services/streams/streamAccessService' +import { + findUserByTargetFactory, + insertInviteAndDeleteOldFactory +} from '@/modules/serverinvites/repositories/serverInvites' +import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' +import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/services/coreResourceCollection' +import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' +import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { Nullable } from '@/modules/shared/helpers/typeHelper' +import { getEventBus } from '@/modules/shared/services/eventBus' +import { publish } from '@/modules/shared/utils/subscriptions' import { BasicTestUser } from '@/test/authHelper' import { ensureError } from '@speckle/shared' import { omit } from 'lodash' +const addStreamCreatedActivity = addStreamCreatedActivityFactory({ + saveActivity: saveActivityFactory({ db }), + publish +}) +const getStream = getStreamFactory({ db }) +const createStream = legacyCreateStreamFactory({ + createStreamReturnRecord: createStreamReturnRecordFactory({ + inviteUsersToProject: inviteUsersToProjectFactory({ + createAndSendInvite: createAndSendInviteFactory({ + findUserByTarget: findUserByTargetFactory(), + insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }), + collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({ + getStream + }), + buildInviteEmailContents: buildCoreInviteEmailContentsFactory({ + getStream + }), + emitEvent: ({ eventName, payload }) => + getEventBus().emit({ + eventName, + payload + }) + }), + getUsers + }), + createStream: createStreamFactory({ db }), + createBranch: createBranchFactory({ db }), + addStreamCreatedActivity, + projectsEventsEmitter: ProjectsEmitter.emit + }) +}) + export type BasicTestStream = { name: string isPublic: boolean From 1a2a3d27df3443014b81dff5189ae6d3f67d89da Mon Sep 17 00:00:00 2001 From: Alexandru Popovici Date: Wed, 9 Oct 2024 12:05:28 +0300 Subject: [PATCH 008/136] Box Rotation (#3205) * Added rotation to speckle box type objects * Addded orthogonal check --- packages/viewer-sandbox/src/main.ts | 4 ++- .../Speckle/SpeckleGeometryConverter.ts | 26 +++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index a4038783f..72583a250 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -105,7 +105,7 @@ const getStream = () => { // prettier-ignore // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D' // Revit sample house (good for bim-like stuff with many display meshes) - 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.dev/streams/c1faab5c62/commits/ab1a1ab2b6' // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.dev/streams/58b5648c4d/commits/60371ecb2d' @@ -423,6 +423,8 @@ const getStream = () => { // 'https://latest.speckle.systems/projects/126cd4b7bb/models/b613d77690@046b56bf12' // 'https://app.speckle.systems/projects/00a5c443d6/models/de56edf901' + + 'https://app.speckle.systems/projects/6cf358a40e/models/e01ffbc891@f1ddc19011' ) } diff --git a/packages/viewer/src/modules/loaders/Speckle/SpeckleGeometryConverter.ts b/packages/viewer/src/modules/loaders/Speckle/SpeckleGeometryConverter.ts index a1148bfa4..b9180e921 100644 --- a/packages/viewer/src/modules/loaders/Speckle/SpeckleGeometryConverter.ts +++ b/packages/viewer/src/modules/loaders/Speckle/SpeckleGeometryConverter.ts @@ -397,7 +397,29 @@ export class SpeckleGeometryConverter extends GeometryConverter { */ const conversionFactor = getConversionFactor(node.raw.units) - const move = this.PointToVector3(node.raw.basePlane.origin) + const T = new Matrix4() + const R = new Matrix4() + + if (node.raw.basePlane) { + T.setPosition(this.PointToVector3(node.raw.basePlane.origin)) + + const eps = 1e-7 + const bX = new Vector3().copy(node.raw.basePlane.xdir) + const bY = new Vector3().copy(node.raw.basePlane.ydir) + const bZ = new Vector3().copy(node.raw.basePlane.normal) + if ( + Math.abs(bX.dot(bY)) < eps && + Math.abs(bX.dot(bZ)) < eps && + Math.abs(bY.dot(bZ)) < eps + ) + R.makeBasis( + node.raw.basePlane.xdir, + node.raw.basePlane.ydir, + node.raw.basePlane.normal + ) + else Logger.warn(`Box ${node.raw.id} does not have orthogonal base plane vectors`) + } else Logger.warn(`Box ${node.raw.id} is missing it's base plane`) + const width = (node.raw.xSize.end - node.raw.xSize.start) * conversionFactor const depth = (node.raw.ySize.end - node.raw.ySize.start) * conversionFactor const height = (node.raw.zSize.end - node.raw.zSize.start) * conversionFactor @@ -442,7 +464,7 @@ export class SpeckleGeometryConverter extends GeometryConverter { attributes: { POSITION: edges }, - bakeTransform: new Matrix4().setPosition(move), + bakeTransform: new Matrix4().copy(T).multiply(R), transform: null } as GeometryData } From ad9cb3aafdc504c448f1517be04697b93bec9897 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Wed, 9 Oct 2024 12:37:57 +0300 Subject: [PATCH 009/136] chore(server): IoC 33 - deleteStreamAndNotifyFactory --- .../tests/activitySummary.spec.ts | 5 +- .../modules/core/domain/streams/operations.ts | 12 +++ .../modules/core/graph/resolvers/projects.ts | 17 +++- .../modules/core/graph/resolvers/streams.ts | 17 +++- .../modules/core/repositories/streams.ts | 21 +++-- .../server/modules/core/services/streams.js | 10 -- .../core/services/streams/management.ts | 91 ++++++++++--------- .../server/modules/core/services/users.js | 5 +- .../server/modules/core/tests/streams.spec.ts | 5 +- .../workspaces/graph/resolvers/workspaces.ts | 5 +- .../modules/workspaces/services/management.ts | 4 +- 11 files changed, 113 insertions(+), 79 deletions(-) diff --git a/packages/server/modules/activitystream/tests/activitySummary.spec.ts b/packages/server/modules/activitystream/tests/activitySummary.spec.ts index 95f820d29..559165f10 100644 --- a/packages/server/modules/activitystream/tests/activitySummary.spec.ts +++ b/packages/server/modules/activitystream/tests/activitySummary.spec.ts @@ -6,7 +6,6 @@ import { sendActivityNotificationsFactory } from '@/modules/activitystream/services/summary' import { expect } from 'chai' -import { deleteStream } from '@/modules/core/services/streams' import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types' import { ActivityDigestMessage, @@ -21,6 +20,7 @@ import { import { db } from '@/db/knex' import { createStreamFactory, + deleteStreamFactory, getStreamFactory } from '@/modules/core/repositories/streams' import { @@ -82,6 +82,7 @@ const createStream = legacyCreateStreamFactory({ projectsEventsEmitter: ProjectsEmitter.emit }) }) +const deleteStream = deleteStreamFactory({ db }) describe('Activity summary @activity', () => { const userA: BasicTestUser = { @@ -163,7 +164,7 @@ describe('Activity summary @activity', () => { info: {}, message: 'foo' }) - await deleteStream({ streamId }) + await deleteStream(streamId) const summary = await createActivitySummary({ userId: userA.id, streamIds: [streamId], diff --git a/packages/server/modules/core/domain/streams/operations.ts b/packages/server/modules/core/domain/streams/operations.ts index 2238a857c..b699f81b4 100644 --- a/packages/server/modules/core/domain/streams/operations.ts +++ b/packages/server/modules/core/domain/streams/operations.ts @@ -9,6 +9,7 @@ import { ProjectCreateInput, StreamCreateInput } from '@/modules/core/graph/generated/graphql' +import { ContextResourceAccessRules } from '@/modules/core/helpers/token' import { MaybeNullOrUndefined, Optional, StreamRoles } from '@speckle/shared' import { Knex } from 'knex' @@ -53,6 +54,8 @@ export type StoreStream = ( }> ) => Promise +export type DeleteStreamRecords = (streamId: string) => Promise + export type CreateStream = ( params: (StreamCreateInput | ProjectCreateInput) & { ownerId: string @@ -66,3 +69,12 @@ export type CreateStream = ( export type LegacyCreateStream = ( params: StreamCreateInput & { ownerId: string } ) => Promise + +export type DeleteStream = ( + streamId: string, + deleterId: string, + deleterResourceAccessRules: ContextResourceAccessRules, + options?: { + skipAccessChecks?: boolean + } +) => Promise diff --git a/packages/server/modules/core/graph/resolvers/projects.ts b/packages/server/modules/core/graph/resolvers/projects.ts index e026dd961..942878a2b 100644 --- a/packages/server/modules/core/graph/resolvers/projects.ts +++ b/packages/server/modules/core/graph/resolvers/projects.ts @@ -1,6 +1,9 @@ import { db } from '@/db/knex' import { saveActivityFactory } from '@/modules/activitystream/repositories' -import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { + addStreamCreatedActivityFactory, + addStreamDeletedActivity +} from '@/modules/activitystream/services/streamActivity' import { RateLimitError } from '@/modules/core/errors/ratelimit' import { StreamNotFoundError } from '@/modules/core/errors/stream' import { WorkspacesModuleDisabledError } from '@/modules/core/errors/workspaces' @@ -19,7 +22,8 @@ import { getUserStreams, getStreamFactory, getStreamCollaboratorsFactory, - createStreamFactory + createStreamFactory, + deleteStreamFactory } from '@/modules/core/repositories/streams' import { getUsers } from '@/modules/core/repositories/users' import { @@ -28,13 +32,14 @@ import { } from '@/modules/core/services/ratelimiter' import { createStreamReturnRecordFactory, - deleteStreamAndNotify, + deleteStreamAndNotifyFactory, updateStreamAndNotify, updateStreamRoleAndNotify } from '@/modules/core/services/streams/management' import { createOnboardingStream } from '@/modules/core/services/streams/onboarding' import { removeStreamCollaborator } from '@/modules/core/services/streams/streamAccessService' import { + deleteAllResourceInvitesFactory, findUserByTargetFactory, insertInviteAndDeleteOldFactory } from '@/modules/serverinvites/repositories/serverInvites' @@ -82,6 +87,12 @@ const createStreamReturnRecord = createStreamReturnRecordFactory({ }), projectsEventsEmitter: ProjectsEmitter.emit }) +const deleteStreamAndNotify = deleteStreamAndNotifyFactory({ + deleteStream: deleteStreamFactory({ db }), + authorizeResolver, + addStreamDeletedActivity, + deleteAllResourceInvites: deleteAllResourceInvitesFactory({ db }) +}) export = { Query: { diff --git a/packages/server/modules/core/graph/resolvers/streams.ts b/packages/server/modules/core/graph/resolvers/streams.ts index 1c14b2047..c39c2fc16 100644 --- a/packages/server/modules/core/graph/resolvers/streams.ts +++ b/packages/server/modules/core/graph/resolvers/streams.ts @@ -29,11 +29,12 @@ import { getUserStreamsCount, getUserStreams, getStreamFactory, - createStreamFactory + createStreamFactory, + deleteStreamFactory } from '@/modules/core/repositories/streams' import { createStreamReturnRecordFactory, - deleteStreamAndNotify, + deleteStreamAndNotifyFactory, updateStreamAndNotify, updateStreamRoleAndNotify } from '@/modules/core/services/streams/management' @@ -50,6 +51,7 @@ import { UserStreamsArgs } from '@/modules/core/graph/generated/graphql' import { + deleteAllResourceInvitesFactory, findUserByTargetFactory, insertInviteAndDeleteOldFactory, queryAllResourceInvitesFactory @@ -63,7 +65,10 @@ import { collectAndValidateCoreTargetsFactory } from '@/modules/serverinvites/se import { buildCoreInviteEmailContentsFactory } from '@/modules/serverinvites/services/coreEmailContents' import { getEventBus } from '@/modules/shared/services/eventBus' import { createBranchFactory } from '@/modules/core/repositories/branches' -import { addStreamCreatedActivityFactory } from '@/modules/activitystream/services/streamActivity' +import { + addStreamCreatedActivityFactory, + addStreamDeletedActivity +} from '@/modules/activitystream/services/streamActivity' import { saveActivityFactory } from '@/modules/activitystream/repositories' import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' @@ -95,6 +100,12 @@ const createStreamReturnRecord = createStreamReturnRecordFactory({ }), projectsEventsEmitter: ProjectsEmitter.emit }) +const deleteStreamAndNotify = deleteStreamAndNotifyFactory({ + deleteStream: deleteStreamFactory({ db }), + authorizeResolver, + addStreamDeletedActivity, + deleteAllResourceInvites: deleteAllResourceInvitesFactory({ db }) +}) const getUserStreamsCore = async ( forOtherUser: boolean, diff --git a/packages/server/modules/core/repositories/streams.ts b/packages/server/modules/core/repositories/streams.ts index 864415b5c..48070ffea 100644 --- a/packages/server/modules/core/repositories/streams.ts +++ b/packages/server/modules/core/repositories/streams.ts @@ -77,7 +77,8 @@ import { GetCommitStreams, GetStream, GetStreamCollaborators, - GetStreams + GetStreams, + DeleteStreamRecords } from '@/modules/core/domain/streams/operations' export type { StreamWithOptionalRole, StreamWithCommitId } @@ -905,20 +906,22 @@ export async function getUserStreamCounts(params: { return _.mapValues(_.keyBy(results, 'userId'), (r) => parseInt(r.count)) } -export async function deleteStream(streamId: string) { - // Delete stream commits (not automatically cascaded) - await knex.raw( - ` +export const deleteStreamFactory = + (deps: { db: Knex }): DeleteStreamRecords => + async (streamId: string) => { + // Delete stream commits (not automatically cascaded) + await deps.db.raw( + ` DELETE FROM commits WHERE id IN ( SELECT sc."commitId" FROM streams s INNER JOIN stream_commits sc ON s.id = sc."streamId" WHERE s.id = ? ) `, - [streamId] - ) - return await Streams.knex().where(Streams.col.id, streamId).del() -} + [streamId] + ) + return await tables.streams(deps.db).where(Streams.col.id, streamId).del() + } export async function getStreamsSourceApps(streamIds: string[]) { if (!streamIds?.length) return {} diff --git a/packages/server/modules/core/services/streams.js b/packages/server/modules/core/services/streams.js index bd0b145ef..6617c83be 100644 --- a/packages/server/modules/core/services/streams.js +++ b/packages/server/modules/core/services/streams.js @@ -6,14 +6,12 @@ const { getFavoritedStreamsCount, setStreamFavorited, canUserFavoriteStream, - deleteStream: deleteStreamFromDb, updateStream: updateStreamInDb, revokeStreamPermissions, grantStreamPermissions, getStreamFactory } = require('@/modules/core/repositories/streams') const { UnauthorizedError, InvalidArgumentError } = require('@/modules/shared/errors') -const { dbLogger } = require('@/logging/logging') const { isResourceAllowed } = require('@/modules/core/helpers/token') const { TokenResourceIdentifierType @@ -52,14 +50,6 @@ module.exports = { return await revokeStreamPermissions({ streamId, userId }) }, - /** - * @deprecated Use deleteStreamAndNotify or use the repository function directly - */ - async deleteStream({ streamId }) { - dbLogger.info('Deleting stream %s', streamId) - return await deleteStreamFromDb(streamId) - }, - /** * @param {Object} p * @param {string | Date | null} [p.cursor] diff --git a/packages/server/modules/core/services/streams/management.ts b/packages/server/modules/core/services/streams/management.ts index 7448ff6b7..ea791bf42 100644 --- a/packages/server/modules/core/services/streams/management.ts +++ b/packages/server/modules/core/services/streams/management.ts @@ -14,11 +14,7 @@ import { StreamUpdatePermissionInput } from '@/modules/core/graph/generated/graphql' import { StreamRecord } from '@/modules/core/helpers/types' -import { - deleteStream, - getStreamFactory, - updateStream -} from '@/modules/core/repositories/streams' +import { getStreamFactory, updateStream } from '@/modules/core/repositories/streams' import { StreamInvalidAccessError, StreamUpdateError @@ -35,7 +31,6 @@ import { isNewResourceAllowed } from '@/modules/core/helpers/token' import { authorizeResolver } from '@/modules/shared' -import { deleteAllResourceInvitesFactory } from '@/modules/serverinvites/repositories/serverInvites' import db from '@/db/knex' import { TokenResourceIdentifier, @@ -49,10 +44,14 @@ import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/pr import { ProjectInviteResourceType } from '@/modules/serverinvites/domain/constants' import { CreateStream, + DeleteStream, + DeleteStreamRecords, LegacyCreateStream, StoreStream } from '@/modules/core/domain/streams/operations' import { StoreBranch } from '@/modules/core/domain/branches/operations' +import { AuthorizeResolver } from '@/modules/shared/domain/operations' +import { DeleteAllResourceInvites } from '@/modules/serverinvites/domain/operations' export const createStreamReturnRecordFactory = (deps: { @@ -136,46 +135,50 @@ export const legacyCreateStreamFactory = /** * Delete stream & notify users (emit events & save activity) - * @param {string} streamId - * @param {string} deleterId */ -export async function deleteStreamAndNotify( - streamId: string, - deleterId: string, - deleterResourceAccessRules: ContextResourceAccessRules, - options?: { - skipAccessChecks?: boolean +export const deleteStreamAndNotifyFactory = + (deps: { + deleteStream: DeleteStreamRecords + authorizeResolver: AuthorizeResolver + addStreamDeletedActivity: typeof addStreamDeletedActivity + deleteAllResourceInvites: DeleteAllResourceInvites + }): DeleteStream => + async ( + streamId: string, + deleterId: string, + deleterResourceAccessRules: ContextResourceAccessRules, + options?: { + skipAccessChecks?: boolean + } + ) => { + const { skipAccessChecks = false } = options || {} + + if (!skipAccessChecks) { + await deps.authorizeResolver( + deleterId, + streamId, + Roles.Stream.Owner, + deleterResourceAccessRules + ) + } + + await deps.addStreamDeletedActivity({ streamId, deleterId }) + + // TODO: this has been around since before my time, we should get rid of it... + // delay deletion by a bit so we can do auth checks + await wait(250) + + // Delete after event so we can do authz + const deleteAllStreamInvites = deps.deleteAllResourceInvites + await Promise.all([ + deleteAllStreamInvites({ + resourceId: streamId, + resourceType: ProjectInviteResourceType + }), + deps.deleteStream(streamId) + ]) + return true } -) { - const { skipAccessChecks = false } = options || {} - - if (!skipAccessChecks) { - await authorizeResolver( - deleterId, - streamId, - Roles.Stream.Owner, - deleterResourceAccessRules - ) - } - - await addStreamDeletedActivity({ streamId, deleterId }) - - // TODO: this has been around since before my time, we should get rid of it... - // delay deletion by a bit so we can do auth checks - await wait(250) - - // TODO: use proper injection once we refactor this module - // Delete after event so we can do authz - const deleteAllStreamInvites = deleteAllResourceInvitesFactory({ db }) - await Promise.all([ - deleteAllStreamInvites({ - resourceId: streamId, - resourceType: ProjectInviteResourceType - }), - deleteStream(streamId) - ]) - return true -} /** * Update stream metadata & notify users (emit events & save activity) diff --git a/packages/server/modules/core/services/users.js b/packages/server/modules/core/services/users.js index 8d269d0ca..a6f59e988 100644 --- a/packages/server/modules/core/services/users.js +++ b/packages/server/modules/core/services/users.js @@ -16,7 +16,6 @@ const { const Users = () => UsersSchema.knex() const Acl = () => ServerAclSchema.knex() -const { deleteStream } = require('./streams') const { LIMITED_USER_FIELDS } = require('@/modules/core/helpers/userHelper') const { getUserByEmail, @@ -58,6 +57,7 @@ const { } = require('@/modules/emails/repositories') const { renderEmail } = require('@/modules/emails/services/emailRendering') const { sendEmail } = require('@/modules/emails/services/sending') +const { deleteStreamFactory } = require('@/modules/core/repositories/streams') const _changeUserRole = async ({ userId, role }) => await Acl().where({ userId }).update({ role }) @@ -325,6 +325,7 @@ module.exports = { * @param {{ deleteAllUserInvites: import('@/modules/serverinvites/domain/operations').DeleteAllUserInvites }} param0 */ deleteUser({ deleteAllUserInvites }) { + const deleteStream = deleteStreamFactory({ db }) return async (id) => { //TODO: check for the last admin user to survive dbLogger.info('Deleting user ' + id) @@ -351,7 +352,7 @@ module.exports = { [id] ) for (const i in streams.rows) { - await deleteStream({ streamId: streams.rows[i].id }) + await deleteStream(streams.rows[i].id) } // Delete all invites (they don't have a FK, so we need to do this manually) diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index 34f516f2f..5a99ff6f1 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -1,7 +1,6 @@ import { expect } from 'chai' import { updateStream, - deleteStream, getStreamUsers, grantPermissionsStream } from '@/modules/core/services/streams' @@ -28,6 +27,7 @@ import { import { StreamWithOptionalRole, createStreamFactory, + deleteStreamFactory, getStreamFactory, markBranchStreamUpdated, markCommitStreamUpdated, @@ -153,6 +153,7 @@ const createStream = legacyCreateStreamFactory({ projectsEventsEmitter: ProjectsEmitter.emit }) }) +const deleteStream = deleteStreamFactory({ db }) describe('Streams @core-streams', () => { const userOne: BasicTestUser = { @@ -253,7 +254,7 @@ describe('Streams @core-streams', () => { ownerId: userOne.id }) - await deleteStream({ streamId: id }) + await deleteStream(id) const stream = await getStream({ streamId: id }) expect(stream).to.not.be.ok diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index 09a830b0e..43f88176d 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -9,7 +9,8 @@ import { updateProjectFactory, upsertProjectRoleFactory, getRolesByUserIdFactory, - getStreamFactory + getStreamFactory, + deleteStreamFactory } from '@/modules/core/repositories/streams' import { getUser, getUsers } from '@/modules/core/repositories/users' import { getStreams } from '@/modules/core/services/streams' @@ -109,7 +110,6 @@ import { } from '@/modules/workspaces/services/retrieval' import { Roles, WorkspaceRoles, removeNullOrUndefinedKeys } from '@speckle/shared' import { chunk } from 'lodash' -import { deleteStream } from '@/modules/core/repositories/streams' import { findEmailsByUserIdFactory, findVerifiedEmailsByUserIdFactory, @@ -184,6 +184,7 @@ const buildCreateAndSendWorkspaceInvite = () => payload }) }) +const deleteStream = deleteStreamFactory({ db }) const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags() diff --git a/packages/server/modules/workspaces/services/management.ts b/packages/server/modules/workspaces/services/management.ts index d45206404..e4273e9c4 100644 --- a/packages/server/modules/workspaces/services/management.ts +++ b/packages/server/modules/workspaces/services/management.ts @@ -26,7 +26,6 @@ import { validateWorkspaceSlug } from '@speckle/shared' import cryptoRandomString from 'crypto-random-string' -import { deleteStream } from '@/modules/core/repositories/streams' import { DeleteWorkspaceRole, GetWorkspaceRoleForUser, @@ -64,6 +63,7 @@ import { chunk, isEmpty, omit } from 'lodash' import { userEmailsCompliantWithWorkspaceDomains } from '@/modules/workspaces/domain/logic' import { workspaceRoles as workspaceRoleDefinitions } from '@/modules/workspaces/roles' import { blockedDomains } from '@speckle/shared' +import { DeleteStreamRecords } from '@/modules/core/domain/streams/operations' type WorkspaceCreateArgs = { userId: string @@ -278,7 +278,7 @@ export const deleteWorkspaceFactory = deleteAllResourceInvites }: { deleteWorkspace: DeleteWorkspace - deleteProject: typeof deleteStream + deleteProject: DeleteStreamRecords queryAllWorkspaceProjects: QueryAllWorkspaceProjects deleteAllResourceInvites: DeleteAllResourceInvites }) => From a3252f88f3eee87b5b77253d6777f8911643fe79 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Wed, 9 Oct 2024 12:54:10 +0300 Subject: [PATCH 010/136] chore(server): IoC 34 - updateStreamAndNotifyFactory --- .../comments/tests/comments.graph.spec.js | 15 ++-- .../modules/core/domain/streams/operations.ts | 20 ++++- .../modules/core/graph/resolvers/projects.ts | 14 ++- .../modules/core/graph/resolvers/streams.ts | 14 ++- .../modules/core/repositories/streams.ts | 87 +++++++++---------- .../server/modules/core/services/streams.js | 10 --- .../core/services/streams/management.ts | 85 ++++++++++-------- .../core/services/streams/onboarding.ts | 3 +- .../server/modules/core/tests/streams.spec.ts | 15 ++-- 9 files changed, 153 insertions(+), 110 deletions(-) diff --git a/packages/server/modules/comments/tests/comments.graph.spec.js b/packages/server/modules/comments/tests/comments.graph.spec.js index cd175eb0a..3c0c38191 100644 --- a/packages/server/modules/comments/tests/comments.graph.spec.js +++ b/packages/server/modules/comments/tests/comments.graph.spec.js @@ -4,10 +4,7 @@ const crs = require('crypto-random-string') const { buildApolloServer } = require('@/app') const { beforeEachContext } = require('@/test/hooks') const { Roles } = require('@/modules/core/helpers/mainConstants') -const { - grantPermissionsStream, - updateStream -} = require('@/modules/core/services/streams') +const { grantPermissionsStream } = require('@/modules/core/services/streams') const { createUser } = require('@/modules/core/services/users') const { gql } = require('graphql-tag') const { createObject } = require('@/modules/core/services/objects') @@ -54,7 +51,8 @@ const { const { markCommitStreamUpdated, getStreamFactory, - createStreamFactory + createStreamFactory, + updateStreamFactory } = require('@/modules/core/repositories/streams') const { VersionsEmitter } = require('@/modules/core/events/versionsEmitter') const { @@ -63,7 +61,8 @@ const { const { getObjectFactory } = require('@/modules/core/repositories/objects') const { legacyCreateStreamFactory, - createStreamReturnRecordFactory + createStreamReturnRecordFactory, + legacyUpdateStreamFactory } = require('@/modules/core/services/streams/management') const { inviteUsersToProjectFactory @@ -157,6 +156,10 @@ const createStream = legacyCreateStreamFactory({ }) }) +const updateStream = legacyUpdateStreamFactory({ + updateStream: updateStreamFactory({ db }) +}) + function buildCommentInputFromString(textString) { return convertBasicStringToDocument(textString) } diff --git a/packages/server/modules/core/domain/streams/operations.ts b/packages/server/modules/core/domain/streams/operations.ts index b699f81b4..b875c2c00 100644 --- a/packages/server/modules/core/domain/streams/operations.ts +++ b/packages/server/modules/core/domain/streams/operations.ts @@ -7,10 +7,12 @@ import { import { TokenResourceIdentifier } from '@/modules/core/domain/tokens/types' import { ProjectCreateInput, - StreamCreateInput + ProjectUpdateInput, + StreamCreateInput, + StreamUpdateInput } from '@/modules/core/graph/generated/graphql' import { ContextResourceAccessRules } from '@/modules/core/helpers/token' -import { MaybeNullOrUndefined, Optional, StreamRoles } from '@speckle/shared' +import { MaybeNullOrUndefined, Nullable, Optional, StreamRoles } from '@speckle/shared' import { Knex } from 'knex' export type GetStreams = ( @@ -56,6 +58,10 @@ export type StoreStream = ( export type DeleteStreamRecords = (streamId: string) => Promise +export type UpdateStreamRecord = ( + update: StreamUpdateInput | ProjectUpdateInput +) => Promise> + export type CreateStream = ( params: (StreamCreateInput | ProjectCreateInput) & { ownerId: string @@ -78,3 +84,13 @@ export type DeleteStream = ( skipAccessChecks?: boolean } ) => Promise + +export type UpdateStream = ( + update: StreamUpdateInput | ProjectUpdateInput, + updaterId: string, + updaterResourceAccessRules: ContextResourceAccessRules +) => Promise + +export type LegacyUpdateStream = ( + update: StreamUpdateInput +) => Promise> diff --git a/packages/server/modules/core/graph/resolvers/projects.ts b/packages/server/modules/core/graph/resolvers/projects.ts index 942878a2b..3e93bd0ab 100644 --- a/packages/server/modules/core/graph/resolvers/projects.ts +++ b/packages/server/modules/core/graph/resolvers/projects.ts @@ -2,7 +2,8 @@ import { db } from '@/db/knex' import { saveActivityFactory } from '@/modules/activitystream/repositories' import { addStreamCreatedActivityFactory, - addStreamDeletedActivity + addStreamDeletedActivity, + addStreamUpdatedActivity } from '@/modules/activitystream/services/streamActivity' import { RateLimitError } from '@/modules/core/errors/ratelimit' import { StreamNotFoundError } from '@/modules/core/errors/stream' @@ -23,7 +24,8 @@ import { getStreamFactory, getStreamCollaboratorsFactory, createStreamFactory, - deleteStreamFactory + deleteStreamFactory, + updateStreamFactory } from '@/modules/core/repositories/streams' import { getUsers } from '@/modules/core/repositories/users' import { @@ -33,7 +35,7 @@ import { import { createStreamReturnRecordFactory, deleteStreamAndNotifyFactory, - updateStreamAndNotify, + updateStreamAndNotifyFactory, updateStreamRoleAndNotify } from '@/modules/core/services/streams/management' import { createOnboardingStream } from '@/modules/core/services/streams/onboarding' @@ -93,6 +95,12 @@ const deleteStreamAndNotify = deleteStreamAndNotifyFactory({ addStreamDeletedActivity, deleteAllResourceInvites: deleteAllResourceInvitesFactory({ db }) }) +const updateStreamAndNotify = updateStreamAndNotifyFactory({ + authorizeResolver, + getStream, + updateStream: updateStreamFactory({ db }), + addStreamUpdatedActivity +}) export = { Query: { diff --git a/packages/server/modules/core/graph/resolvers/streams.ts b/packages/server/modules/core/graph/resolvers/streams.ts index c39c2fc16..1a6a1bef3 100644 --- a/packages/server/modules/core/graph/resolvers/streams.ts +++ b/packages/server/modules/core/graph/resolvers/streams.ts @@ -30,12 +30,13 @@ import { getUserStreams, getStreamFactory, createStreamFactory, - deleteStreamFactory + deleteStreamFactory, + updateStreamFactory } from '@/modules/core/repositories/streams' import { createStreamReturnRecordFactory, deleteStreamAndNotifyFactory, - updateStreamAndNotify, + updateStreamAndNotifyFactory, updateStreamRoleAndNotify } from '@/modules/core/services/streams/management' import { adminOverrideEnabled } from '@/modules/shared/helpers/envHelper' @@ -67,7 +68,8 @@ import { getEventBus } from '@/modules/shared/services/eventBus' import { createBranchFactory } from '@/modules/core/repositories/branches' import { addStreamCreatedActivityFactory, - addStreamDeletedActivity + addStreamDeletedActivity, + addStreamUpdatedActivity } from '@/modules/activitystream/services/streamActivity' import { saveActivityFactory } from '@/modules/activitystream/repositories' import { ProjectsEmitter } from '@/modules/core/events/projectsEmitter' @@ -106,6 +108,12 @@ const deleteStreamAndNotify = deleteStreamAndNotifyFactory({ addStreamDeletedActivity, deleteAllResourceInvites: deleteAllResourceInvitesFactory({ db }) }) +const updateStreamAndNotify = updateStreamAndNotifyFactory({ + authorizeResolver, + getStream, + updateStream: updateStreamFactory({ db }), + addStreamUpdatedActivity +}) const getUserStreamsCore = async ( forOtherUser: boolean, diff --git a/packages/server/modules/core/repositories/streams.ts b/packages/server/modules/core/repositories/streams.ts index 48070ffea..0f7aaf96d 100644 --- a/packages/server/modules/core/repositories/streams.ts +++ b/packages/server/modules/core/repositories/streams.ts @@ -78,7 +78,8 @@ import { GetStream, GetStreamCollaborators, GetStreams, - DeleteStreamRecords + DeleteStreamRecords, + UpdateStreamRecord } from '@/modules/core/domain/streams/operations' export type { StreamWithOptionalRole, StreamWithCommitId } @@ -955,58 +956,56 @@ const isProjectUpdateInput = ( i: StreamUpdateInput | ProjectUpdateInput ): i is ProjectUpdateInput => has(i, 'visibility') -/** @deprecated Replace all calls with `updateProjectFacotry` */ -export async function updateStream( - update: StreamUpdateInput | ProjectUpdateInput, - db?: Knex -) { - const { id: streamId } = update +export const updateStreamFactory = + (deps: { db: Knex }): UpdateStreamRecord => + async (update: StreamUpdateInput | ProjectUpdateInput) => { + const { id: streamId } = update - if (!update.name) update.name = null // to prevent saving name '' - const validUpdate: Partial & Record = omitBy( - update, - (v) => isNull(v) || isUndefined(v) - ) + if (!update.name) update.name = null // to prevent saving name '' + const validUpdate: Partial & Record = omitBy( + update, + (v) => isNull(v) || isUndefined(v) + ) - if (isProjectUpdateInput(update)) { - if (has(validUpdate, 'visibility')) { - validUpdate.isPublic = update.visibility !== ProjectVisibility.Private - validUpdate.isDiscoverable = update.visibility === ProjectVisibility.Public - delete validUpdate['visibility'] // cause it's not a real column + if (isProjectUpdateInput(update)) { + if (has(validUpdate, 'visibility')) { + validUpdate.isPublic = update.visibility !== ProjectVisibility.Private + validUpdate.isDiscoverable = update.visibility === ProjectVisibility.Public + delete validUpdate['visibility'] // cause it's not a real column + } + } else { + if (has(validUpdate, 'isPublic') && !validUpdate.isPublic) { + validUpdate.isDiscoverable = false + } } - } else { + if (has(validUpdate, 'isPublic') && !validUpdate.isPublic) { - validUpdate.isDiscoverable = false + validUpdate.allowPublicComments = false + } else if ( + has(validUpdate, 'allowPublicComments') && + validUpdate.allowPublicComments + ) { + validUpdate.isPublic = true } + + if (!Object.keys(validUpdate).length) return null + + const [updatedStream] = await tables + .streams(deps.db) + .returning('*') + .where({ id: streamId }) + .update({ + ...validUpdate, + updatedAt: knex.fn.now() + }) + + return updatedStream } - if (has(validUpdate, 'isPublic') && !validUpdate.isPublic) { - validUpdate.allowPublicComments = false - } else if ( - has(validUpdate, 'allowPublicComments') && - validUpdate.allowPublicComments - ) { - validUpdate.isPublic = true - } - - if (!Object.keys(validUpdate).length) return null - - const [updatedStream] = await tables - .streams(db ?? knex) - .returning('*') - .where({ id: streamId }) - .update({ - ...validUpdate, - updatedAt: knex.fn.now() - }) - - return updatedStream -} - export const updateProjectFactory = ({ db }: { db: Knex }): UpdateProject => async ({ projectUpdate }) => { - const updatedStream = await updateStream(projectUpdate, db) + const updatedStream = await updateStreamFactory({ db })(projectUpdate) if (!updatedStream) { throw new StreamUpdateError() @@ -1209,7 +1208,7 @@ export async function markOnboardingBaseStream(streamId: string, version: string if (!stream) { throw new Error(`Stream ${streamId} not found`) } - await updateStream({ + await updateStreamFactory({ db })({ id: streamId, name: 'Onboarding Stream Local Source - Do Not Delete' }) diff --git a/packages/server/modules/core/services/streams.js b/packages/server/modules/core/services/streams.js index 6617c83be..aba3798d1 100644 --- a/packages/server/modules/core/services/streams.js +++ b/packages/server/modules/core/services/streams.js @@ -6,7 +6,6 @@ const { getFavoritedStreamsCount, setStreamFavorited, canUserFavoriteStream, - updateStream: updateStreamInDb, revokeStreamPermissions, grantStreamPermissions, getStreamFactory @@ -25,15 +24,6 @@ const { */ module.exports = { - /** - * @deprecated Use updateStreamAndNotify or use the repository function directly - * @param {import('@/modules/core/graph/generated/graphql').StreamUpdateInput} update - */ - async updateStream(update) { - const updatedStream = await updateStreamInDb(update) - return updatedStream?.id || null - }, - setStreamFavorited, /** diff --git a/packages/server/modules/core/services/streams/management.ts b/packages/server/modules/core/services/streams/management.ts index ea791bf42..2f1c723ee 100644 --- a/packages/server/modules/core/services/streams/management.ts +++ b/packages/server/modules/core/services/streams/management.ts @@ -14,7 +14,6 @@ import { StreamUpdatePermissionInput } from '@/modules/core/graph/generated/graphql' import { StreamRecord } from '@/modules/core/helpers/types' -import { getStreamFactory, updateStream } from '@/modules/core/repositories/streams' import { StreamInvalidAccessError, StreamUpdateError @@ -30,8 +29,6 @@ import { ContextResourceAccessRules, isNewResourceAllowed } from '@/modules/core/helpers/token' -import { authorizeResolver } from '@/modules/shared' -import db from '@/db/knex' import { TokenResourceIdentifier, TokenResourceIdentifierType @@ -46,8 +43,12 @@ import { CreateStream, DeleteStream, DeleteStreamRecords, + GetStream, LegacyCreateStream, - StoreStream + LegacyUpdateStream, + StoreStream, + UpdateStream, + UpdateStreamRecord } from '@/modules/core/domain/streams/operations' import { StoreBranch } from '@/modules/core/domain/branches/operations' import { AuthorizeResolver } from '@/modules/shared/domain/operations' @@ -183,42 +184,58 @@ export const deleteStreamAndNotifyFactory = /** * Update stream metadata & notify users (emit events & save activity) */ -export async function updateStreamAndNotify( - update: StreamUpdateInput | ProjectUpdateInput, - updaterId: string, - updaterResourceAccessRules: ContextResourceAccessRules -) { - await authorizeResolver( - updaterId, - update.id, - Roles.Stream.Owner, - updaterResourceAccessRules - ) +export const updateStreamAndNotifyFactory = + (deps: { + authorizeResolver: AuthorizeResolver + getStream: GetStream + updateStream: UpdateStreamRecord + addStreamUpdatedActivity: typeof addStreamUpdatedActivity + }): UpdateStream => + async ( + update: StreamUpdateInput | ProjectUpdateInput, + updaterId: string, + updaterResourceAccessRules: ContextResourceAccessRules + ) => { + await deps.authorizeResolver( + updaterId, + update.id, + Roles.Stream.Owner, + updaterResourceAccessRules + ) - const getStream = getStreamFactory({ db }) - const oldStream = await getStream({ streamId: update.id, userId: updaterId }) - if (!oldStream) { - throw new StreamUpdateError('Stream not found', { - info: { updaterId, streamId: update.id } + const oldStream = await deps.getStream({ streamId: update.id, userId: updaterId }) + if (!oldStream) { + throw new StreamUpdateError('Stream not found', { + info: { updaterId, streamId: update.id } + }) + } + + const newStream = await deps.updateStream(update) + if (!newStream) { + return oldStream + } + + await deps.addStreamUpdatedActivity({ + streamId: update.id, + updaterId, + oldStream, + newStream, + update }) + + return newStream } - const newStream = await updateStream(update) - if (!newStream) { - return oldStream +/** + * @deprecated Use updateStreamAndNotifyFactory() or the repo fn directly + */ +export const legacyUpdateStreamFactory = + (deps: { updateStream: UpdateStreamRecord }): LegacyUpdateStream => + async (update) => { + const updatedStream = await deps.updateStream(update) + return updatedStream?.id || null } - await addStreamUpdatedActivity({ - streamId: update.id, - updaterId, - oldStream, - newStream, - update - }) - - return newStream -} - type PermissionUpdateInput = | StreamUpdatePermissionInput | StreamRevokePermissionInput diff --git a/packages/server/modules/core/services/streams/onboarding.ts b/packages/server/modules/core/services/streams/onboarding.ts index cc36386fb..af385539e 100644 --- a/packages/server/modules/core/services/streams/onboarding.ts +++ b/packages/server/modules/core/services/streams/onboarding.ts @@ -11,7 +11,7 @@ import { createStreamFactory, getOnboardingBaseStream, getStreamFactory, - updateStream + updateStreamFactory } from '@/modules/core/repositories/streams' import { getUser } from '@/modules/core/services/users' import { @@ -51,6 +51,7 @@ export async function createOnboardingStream( ) } + const updateStream = updateStreamFactory({ db }) const addStreamCreatedActivity = addStreamCreatedActivityFactory({ saveActivity: saveActivityFactory({ db }), publish diff --git a/packages/server/modules/core/tests/streams.spec.ts b/packages/server/modules/core/tests/streams.spec.ts index 5a99ff6f1..efe3e0394 100644 --- a/packages/server/modules/core/tests/streams.spec.ts +++ b/packages/server/modules/core/tests/streams.spec.ts @@ -1,9 +1,5 @@ import { expect } from 'chai' -import { - updateStream, - getStreamUsers, - grantPermissionsStream -} from '@/modules/core/services/streams' +import { getStreamUsers, grantPermissionsStream } from '@/modules/core/services/streams' import { createObject } from '@/modules/core/services/objects' @@ -31,7 +27,8 @@ import { getStreamFactory, markBranchStreamUpdated, markCommitStreamUpdated, - revokeStreamPermissions + revokeStreamPermissions, + updateStreamFactory } from '@/modules/core/repositories/streams' import { has, times } from 'lodash' import { Streams } from '@/modules/core/dbSchema' @@ -75,7 +72,8 @@ import { addCommitCreatedActivity } from '@/modules/activitystream/services/comm import { getObjectFactory } from '@/modules/core/repositories/objects' import { createStreamReturnRecordFactory, - legacyCreateStreamFactory + legacyCreateStreamFactory, + legacyUpdateStreamFactory } from '@/modules/core/services/streams/management' import { inviteUsersToProjectFactory } from '@/modules/serverinvites/services/projectInviteManagement' import { createAndSendInviteFactory } from '@/modules/serverinvites/services/creation' @@ -154,6 +152,9 @@ const createStream = legacyCreateStreamFactory({ }) }) const deleteStream = deleteStreamFactory({ db }) +const updateStream = legacyUpdateStreamFactory({ + updateStream: updateStreamFactory({ db }) +}) describe('Streams @core-streams', () => { const userOne: BasicTestUser = { From b428250a69ef9c35c523ce19a41f6e2258a225a9 Mon Sep 17 00:00:00 2001 From: Benjamin Ottensten Date: Wed, 9 Oct 2024 14:09:58 +0200 Subject: [PATCH 011/136] Update copy in project invite banner (#3210) Old copy felt a bit clumsy and I also didn't like the use of "team" here. --- packages/frontend-2/components/projects/invite/Banner.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-2/components/projects/invite/Banner.vue b/packages/frontend-2/components/projects/invite/Banner.vue index e4ac05802..a9de6d1d7 100644 --- a/packages/frontend-2/components/projects/invite/Banner.vue +++ b/packages/frontend-2/components/projects/invite/Banner.vue @@ -7,7 +7,7 @@ >