From 9fb1ec82fcae7390877ea7ecb9fff5458f0b9c00 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 24 Sep 2024 11:39:08 +0300 Subject: [PATCH] chore(server): comments IoC 7 - convertLegacyDataToStateFactory --- .../services/commentActivity.ts | 58 ++++- .../modules/comments/domain/operations.ts | 37 +++- .../server/modules/comments/domain/types.ts | 9 + .../comments/graph/resolvers/comments.ts | 41 +++- .../modules/comments/repositories/comments.ts | 32 +-- .../server/modules/comments/services/data.ts | 163 +++++++------- packages/server/modules/core/loaders.ts | 3 +- .../core/services/commit/viewerResources.ts | 198 ++++++++++-------- 8 files changed, 348 insertions(+), 193 deletions(-) diff --git a/packages/server/modules/activitystream/services/commentActivity.ts b/packages/server/modules/activitystream/services/commentActivity.ts index 6ba8c8300..0dc566335 100644 --- a/packages/server/modules/activitystream/services/commentActivity.ts +++ b/packages/server/modules/activitystream/services/commentActivity.ts @@ -1,18 +1,23 @@ +import { db } from '@/db/knex' import { ActionTypes, ResourceTypes } from '@/modules/activitystream/helpers/types' import { saveActivity } from '@/modules/activitystream/services' +import { ViewerResourceItem } from '@/modules/comments/domain/types' import { CommentRecord } from '@/modules/comments/helpers/types' +import { getCommentsResourcesFactory } from '@/modules/comments/repositories/comments' import { CommentCreateInput, CreateCommentInput, CreateCommentReplyInput, ProjectCommentsUpdatedMessageType, - ReplyCreateInput, - ViewerResourceItem + ReplyCreateInput } from '@/modules/core/graph/generated/graphql' +import { getCommitsAndTheirBranchIds } from '@/modules/core/repositories/commits' +import { getStreamObjects } from '@/modules/core/repositories/objects' import { getViewerResourceItemsUngrouped, - getViewerResourcesForComment, - getViewerResourcesFromLegacyIdentifiers + getViewerResourcesForCommentFactory, + getViewerResourcesForCommentsFactory, + getViewerResourcesFromLegacyIdentifiersFactory } from '@/modules/core/services/commit/viewerResources' import { pubsub } from '@/modules/shared/utils/subscriptions' import { @@ -39,6 +44,17 @@ export async function addCommentCreatedActivity(params: { }) { const { streamId, userId, input, comment } = params + const getViewerResourcesFromLegacyIdentifiers = + getViewerResourcesFromLegacyIdentifiersFactory({ + getViewerResourcesForComments: getViewerResourcesForCommentsFactory({ + getCommentsResources: getCommentsResourcesFactory({ db }), + getViewerResourcesFromLegacyIdentifiers: (...args) => + getViewerResourcesFromLegacyIdentifiers(...args) // recursive dep + }), + getCommitsAndTheirBranchIds, + getStreamObjects + }) + let resourceIds: string let resourceItems: ViewerResourceItem[] if (isLegacyCommentCreateInput(input)) { @@ -104,6 +120,23 @@ export async function addCommentArchivedActivity(params: { const { streamId, commentId, userId, input, comment } = params const isArchiving = !!input.archived + const getCommentsResources = getCommentsResourcesFactory({ db }) + const getViewerResourcesFromLegacyIdentifiers = + getViewerResourcesFromLegacyIdentifiersFactory({ + getViewerResourcesForComments: getViewerResourcesForCommentsFactory({ + getCommentsResources: getCommentsResourcesFactory({ db }), + getViewerResourcesFromLegacyIdentifiers: (...args) => + getViewerResourcesFromLegacyIdentifiers(...args) // recursive dep + }), + getCommitsAndTheirBranchIds, + getStreamObjects + }) + + const getViewerResourcesForComment = getViewerResourcesForCommentFactory({ + getCommentsResources, + getViewerResourcesFromLegacyIdentifiers + }) + await Promise.all([ saveActivity({ streamId, @@ -149,6 +182,23 @@ export async function addReplyAddedActivity(params: { }) { const { streamId, input, reply, userId } = params + const getCommentsResources = getCommentsResourcesFactory({ db }) + const getViewerResourcesFromLegacyIdentifiers = + getViewerResourcesFromLegacyIdentifiersFactory({ + getViewerResourcesForComments: getViewerResourcesForCommentsFactory({ + getCommentsResources: getCommentsResourcesFactory({ db }), + getViewerResourcesFromLegacyIdentifiers: (...args) => + getViewerResourcesFromLegacyIdentifiers(...args) // recursive dep + }), + getCommitsAndTheirBranchIds, + getStreamObjects + }) + + const getViewerResourcesForComment = getViewerResourcesForCommentFactory({ + getCommentsResources, + getViewerResourcesFromLegacyIdentifiers + }) + const parentCommentId = isLegacyReplyCreateInput(input) ? input.parentComment : input.threadId diff --git a/packages/server/modules/comments/domain/operations.ts b/packages/server/modules/comments/domain/operations.ts index 488b81296..d4d3697bd 100644 --- a/packages/server/modules/comments/domain/operations.ts +++ b/packages/server/modules/comments/domain/operations.ts @@ -1,10 +1,18 @@ -import { ExtendedComment, ResourceIdentifier } from '@/modules/comments/domain/types' +import { + ExtendedComment, + ResourceIdentifier, + ViewerResourceItem +} from '@/modules/comments/domain/types' import { CommentLinkRecord, CommentRecord } from '@/modules/comments/helpers/types' import { SmartTextEditorValueSchema } from '@/modules/core/services/richTextEditorService' import { MarkNullableOptional, Optional } from '@/modules/shared/helpers/typeHelper' +import { LegacyCommentViewerData } from '@/test/graphql/generated/graphql' +import { SpeckleViewer } from '@speckle/shared' import { Knex } from 'knex' import { Merge } from 'type-fest' +type SerializedViewerState = SpeckleViewer.ViewerState.SerializedViewerState + export type GetComment = (params: { id: string userId?: string @@ -44,6 +52,13 @@ export type UpdateComment = ( export type MarkCommentUpdated = (commentId: string) => Promise +export type GetCommentsResources = (commentIds: string[]) => Promise<{ + [commentId: string]: { + commentId: string + resources: ResourceIdentifier[] + } +}> + export type CheckStreamResourcesAccess = (params: { streamId: string resources: ResourceIdentifier[] @@ -53,3 +68,23 @@ export type ValidateInputAttachments = ( streamId: string, blobIds: string[] ) => Promise + +export type GetViewerResourcesForComments = ( + projectId: string, + commentIds: string[] +) => Promise + +export type GetViewerResourcesForComment = ( + projectId: string, + commentId: string +) => Promise + +export type GetViewerResourcesFromLegacyIdentifiers = ( + projectId: string, + resources: Array +) => Promise + +export type ConvertLegacyDataToState = ( + data: Partial, + comment: CommentRecord +) => Promise diff --git a/packages/server/modules/comments/domain/types.ts b/packages/server/modules/comments/domain/types.ts index c588cbd2c..ab895111f 100644 --- a/packages/server/modules/comments/domain/types.ts +++ b/packages/server/modules/comments/domain/types.ts @@ -1,4 +1,5 @@ import { CommentLinkRecord, CommentRecord } from '@/modules/comments/helpers/types' +import { Nullable } from '@speckle/shared' export type ResourceIdentifier = { resourceId: string @@ -24,3 +25,11 @@ export type ExtendedComment = CommentRecord & { */ viewedAt?: Date } + +export type ViewerResourceItem = { + /** Null if resource represents an object */ + modelId?: Nullable + objectId: string + /** Null if resource represents an object */ + versionId?: Nullable +} diff --git a/packages/server/modules/comments/graph/resolvers/comments.ts b/packages/server/modules/comments/graph/resolvers/comments.ts index c4b71a691..614e58ffe 100644 --- a/packages/server/modules/comments/graph/resolvers/comments.ts +++ b/packages/server/modules/comments/graph/resolvers/comments.ts @@ -14,6 +14,7 @@ import { deleteCommentFactory, getCommentFactory, getCommentsLegacyFactory, + getCommentsResourcesFactory, getResourceCommentCountFactory, insertCommentLinksFactory, insertCommentsFactory, @@ -49,8 +50,10 @@ import { } from '@/modules/activitystream/services/commentActivity' import { getViewerResourceItemsUngrouped, - getViewerResourcesForComment, - doViewerResourcesFit + doViewerResourcesFit, + getViewerResourcesForCommentFactory, + getViewerResourcesFromLegacyIdentifiersFactory, + getViewerResourcesForCommentsFactory } from '@/modules/core/services/commit/viewerResources' import { authorizeProjectCommentsAccess, @@ -65,18 +68,17 @@ import { isDataStruct, formatSerializedViewerState, convertStateToLegacyData, - convertLegacyDataToState + convertLegacyDataToStateFactory } from '@/modules/comments/services/data' -import { - Resolvers, - ResourceIdentifier, - ResourceType -} from '@/modules/core/graph/generated/graphql' +import { Resolvers, ResourceType } from '@/modules/core/graph/generated/graphql' import { GraphQLContext } from '@/modules/shared/helpers/typeHelper' import { CommentRecord } from '@/modules/comments/helpers/types' import { db } from '@/db/knex' import { CommentsEmitter } from '@/modules/comments/events/emitter' import { getBlobsFactory } from '@/modules/blobstorage/repositories' +import { ResourceIdentifier } from '@/modules/comments/domain/types' +import { getCommitsAndTheirBranchIds } from '@/modules/core/repositories/commits' +import { getStreamObjects } from '@/modules/core/repositories/objects' const streamResourceCheck = streamResourceCheckFactory({ checkStreamResourceAccess: checkStreamResourceAccessFactory({ db }) @@ -121,6 +123,29 @@ const archiveComment = archiveCommentFactory({ }) const getResourceCommentCount = getResourceCommentCountFactory({ db }) +const getCommentsResources = getCommentsResourcesFactory({ db }) +const getViewerResourcesFromLegacyIdentifiers = + getViewerResourcesFromLegacyIdentifiersFactory({ + getViewerResourcesForComments: getViewerResourcesForCommentsFactory({ + getCommentsResources: getCommentsResourcesFactory({ db }), + getViewerResourcesFromLegacyIdentifiers: (...args) => + getViewerResourcesFromLegacyIdentifiers(...args) // recursive dep + }), + getCommitsAndTheirBranchIds, + getStreamObjects + }) + +const getViewerResourcesForComment = getViewerResourcesForCommentFactory({ + getCommentsResources, + getViewerResourcesFromLegacyIdentifiers +}) +const convertLegacyDataToState = convertLegacyDataToStateFactory({ + getViewerResourcesForComments: getViewerResourcesForCommentsFactory({ + getCommentsResources, + getViewerResourcesFromLegacyIdentifiers + }) +}) + const getStreamComment = async ( { streamId, commentId }: { streamId: string; commentId: string }, ctx: GraphQLContext diff --git a/packages/server/modules/comments/repositories/comments.ts b/packages/server/modules/comments/repositories/comments.ts index 7278849bf..2f6d87bdd 100644 --- a/packages/server/modules/comments/repositories/comments.ts +++ b/packages/server/modules/comments/repositories/comments.ts @@ -36,6 +36,7 @@ import { CheckStreamResourceAccess, DeleteComment, GetComment, + GetCommentsResources, InsertCommentLinks, InsertCommentPayload, InsertComments, @@ -84,22 +85,25 @@ export const getCommentFactory = /** * Get resources array for the specified comments. Results object is keyed by comment ID. */ -export async function getCommentsResources(commentIds: string[]) { - if (!commentIds.length) return {} +export const getCommentsResourcesFactory = + (deps: { db: Knex }): GetCommentsResources => + async (commentIds: string[]) => { + if (!commentIds.length) return {} - const q = CommentLinks.knex() - .select<{ commentId: string; resources: ResourceIdentifier[] }[]>([ - CommentLinks.col.commentId, - knex.raw( - `JSON_AGG(json_build_object('resourceId', "resourceId", 'resourceType', "resourceType")) as resources` - ) - ]) - .whereIn(CommentLinks.col.commentId, commentIds) - .groupBy(CommentLinks.col.commentId) + const q = tables + .commentLinks(deps.db) + .select<{ commentId: string; resources: ResourceIdentifier[] }[]>([ + CommentLinks.col.commentId, + knex.raw( + `JSON_AGG(json_build_object('resourceId', "resourceId", 'resourceType', "resourceType")) as resources` + ) + ]) + .whereIn(CommentLinks.col.commentId, commentIds) + .groupBy(CommentLinks.col.commentId) - const results = await q - return keyBy(results, 'commentId') -} + const results = await q + return keyBy(results, 'commentId') + } export async function getCommentsViewedAt(commentIds: string[], userId: string) { if (!commentIds?.length || !userId) return [] diff --git a/packages/server/modules/comments/services/data.ts b/packages/server/modules/comments/services/data.ts index f14d19a1a..44737e3c7 100644 --- a/packages/server/modules/comments/services/data.ts +++ b/packages/server/modules/comments/services/data.ts @@ -1,9 +1,9 @@ -import { CommentRecord } from '@/modules/comments/helpers/types' -import { LegacyCommentViewerData } from '@/modules/core/graph/generated/graphql' import { - getViewerResourcesForComments, - viewerResourcesToString -} from '@/modules/core/services/commit/viewerResources' + ConvertLegacyDataToState, + GetViewerResourcesForComments +} from '@/modules/comments/domain/operations' +import { LegacyCommentViewerData } from '@/modules/core/graph/generated/graphql' +import { viewerResourcesToString } from '@/modules/core/services/commit/viewerResources' import { Nullable, SpeckleViewer } from '@speckle/shared' import { has, get, intersection, isObjectLike } from 'lodash' @@ -97,82 +97,89 @@ export function convertStateToLegacyData(state: SerializedViewerState): LegacyDa return ret } -export async function convertLegacyDataToState( - data: LegacyData, - comment: CommentRecord -): Promise { - const resources = await getViewerResourcesForComments(comment.streamId, [comment.id]) - const sectionBox = data.filters?.sectionBox || data.sectionBox +export const convertLegacyDataToStateFactory = + (deps: { + getViewerResourcesForComments: GetViewerResourcesForComments + }): ConvertLegacyDataToState => + async (data, comment) => { + const resources = await deps.getViewerResourcesForComments(comment.streamId, [ + comment.id + ]) + const sectionBox = data.filters?.sectionBox || data.sectionBox - const ret: SerializedViewerState = { - projectId: comment.streamId, - sessionId: 'legacy-sessionId', - viewer: { - metadata: { - filteringState: { - passMax: data.filters?.passMax, - passMin: data.filters?.passMin - } - } - }, - resources: { - request: { - resourceIdString: viewerResourcesToString(resources), - threadFilters: { - includeArchived: false, - loadedVersionsOnly: false - } - } - }, - ui: { - threads: { - openThread: { - threadId: null, - isTyping: false, - newThreadEditor: true - } - }, - spotlightUserSessionId: null, - explodeFactor: 0, - filters: { - isolatedObjectIds: data.filters?.isolatedIds || [], - hiddenObjectIds: data.filters?.hiddenIds || [], - selectedObjectIds: [], - propertyFilter: { - key: data.filters?.propertyInfoKey || null, - isApplied: true - } - }, - camera: { - position: [data.camPos?.[0] || 0, data.camPos?.[1] || 0, data.camPos?.[2] || 0], - target: [data.camPos?.[3] || 0, data.camPos?.[4] || 0, data.camPos?.[5] || 0], - isOrthoProjection: !!data.camPos?.[6], - zoom: data.camPos?.[7] || 1 - }, - sectionBox: sectionBox - ? { - min: (sectionBox.min as number[]) || [0, 0, 0], - max: (sectionBox.max as number[]) || [0, 0, 0] + const ret: SerializedViewerState = { + projectId: comment.streamId, + sessionId: 'legacy-sessionId', + viewer: { + metadata: { + filteringState: { + passMax: data.filters?.passMax, + passMin: data.filters?.passMin } - : null, - lightConfig: {}, - selection: data.location - ? [ - data.location.x as number, - data.location.y as number, - data.location.z as number - ] - : null, - diff: { - command: null, - mode: 1, - time: 0.5 + } }, - measurement: { - enabled: false, - options: null + resources: { + request: { + resourceIdString: viewerResourcesToString(resources), + threadFilters: { + includeArchived: false, + loadedVersionsOnly: false + } + } + }, + ui: { + threads: { + openThread: { + threadId: null, + isTyping: false, + newThreadEditor: true + } + }, + spotlightUserSessionId: null, + explodeFactor: 0, + filters: { + isolatedObjectIds: data.filters?.isolatedIds || [], + hiddenObjectIds: data.filters?.hiddenIds || [], + selectedObjectIds: [], + propertyFilter: { + key: data.filters?.propertyInfoKey || null, + isApplied: true + } + }, + camera: { + position: [ + data.camPos?.[0] || 0, + data.camPos?.[1] || 0, + data.camPos?.[2] || 0 + ], + target: [data.camPos?.[3] || 0, data.camPos?.[4] || 0, data.camPos?.[5] || 0], + isOrthoProjection: !!data.camPos?.[6], + zoom: data.camPos?.[7] || 1 + }, + sectionBox: sectionBox + ? { + min: (sectionBox.min as number[]) || [0, 0, 0], + max: (sectionBox.max as number[]) || [0, 0, 0] + } + : null, + lightConfig: {}, + selection: data.location + ? [ + data.location.x as number, + data.location.y as number, + data.location.z as number + ] + : null, + diff: { + command: null, + mode: 1, + time: 0.5 + }, + measurement: { + enabled: false, + options: null + } } } + return ret } - return ret -} diff --git a/packages/server/modules/core/loaders.ts b/packages/server/modules/core/loaders.ts index a12c4d9ae..d65e1682b 100644 --- a/packages/server/modules/core/loaders.ts +++ b/packages/server/modules/core/loaders.ts @@ -36,7 +36,7 @@ import { getCommentParents, getCommentReplyAuthorIds, getCommentReplyCounts, - getCommentsResources, + getCommentsResourcesFactory, getCommentsViewedAt, getCommitCommentCounts, getStreamCommentCountsFactory @@ -98,6 +98,7 @@ const getRevisionsFunctions = getRevisionsFunctionsFactory({ db }) const getFunctionAutomationCounts = getFunctionAutomationCountsFactory({ db }) const getStreamCommentCounts = getStreamCommentCountsFactory({ db }) const getAutomationRunsTriggers = getAutomationRunsTriggersFactory({ db }) +const getCommentsResources = getCommentsResourcesFactory({ db }) /** * TODO: Lazy load DataLoaders to reduce memory usage diff --git a/packages/server/modules/core/services/commit/viewerResources.ts b/packages/server/modules/core/services/commit/viewerResources.ts index c46d3597d..1e2d56d29 100644 --- a/packages/server/modules/core/services/commit/viewerResources.ts +++ b/packages/server/modules/core/services/commit/viewerResources.ts @@ -1,4 +1,9 @@ -import { getCommentsResources } from '@/modules/comments/repositories/comments' +import { + GetCommentsResources, + GetViewerResourcesForComment, + GetViewerResourcesForComments, + GetViewerResourcesFromLegacyIdentifiers +} from '@/modules/comments/domain/operations' import { ResourceIdentifier, ResourceIdentifierInput, @@ -37,31 +42,37 @@ function isResourceIdentifierEqual( return true } -async function getObjectResourceGroups( - projectId: string, - resources: SpeckleViewer.ViewerRoute.ViewerObjectResource[] -) { - const objects = keyBy( - await getStreamObjects( - projectId, - resources.map((r) => r.objectId) - ), - 'id' - ) - - const results: ViewerResourceGroup[] = [] - for (const objectResource of resources) { - if (!objects[objectResource.objectId]) continue - - results.push({ - identifier: objectResource.toString(), - items: [{ modelId: null, versionId: null, objectId: objectResource.objectId }] - }) - } - - return results +type GetObjectResourceGroupsDeps = { + getStreamObjects: typeof getStreamObjects } +const getObjectResourceGroupsFactory = + (deps: GetObjectResourceGroupsDeps) => + async ( + projectId: string, + resources: SpeckleViewer.ViewerRoute.ViewerObjectResource[] + ) => { + const objects = keyBy( + await deps.getStreamObjects( + projectId, + resources.map((r) => r.objectId) + ), + 'id' + ) + + const results: ViewerResourceGroup[] = [] + for (const objectResource of resources) { + if (!objects[objectResource.objectId]) continue + + results.push({ + identifier: objectResource.toString(), + items: [{ modelId: null, versionId: null, objectId: objectResource.objectId }] + }) + } + + return results + } + async function getVersionResourceGroupsIncludingAllVersions( projectId: string, params: { @@ -291,7 +302,7 @@ export async function getViewerResourceGroups( const results: ViewerResourceGroup[] = flatten( await Promise.all([ - getObjectResourceGroups(projectId, objectResources), + getObjectResourceGroupsFactory({ getStreamObjects })(projectId, objectResources), getVersionResourceGroups( projectId, { modelResources, folderResources, allModelsResource }, @@ -318,76 +329,89 @@ export async function getViewerResourceItemsUngrouped( return uniqWith(results, isResourceItemEqual) } -export async function getViewerResourcesFromLegacyIdentifiers( - projectId: string, - resources: Array -): Promise { - if (!resources.length || !projectId) return [] +export const getViewerResourcesFromLegacyIdentifiersFactory = + ( + deps: { + getViewerResourcesForComments: GetViewerResourcesForComments + getCommitsAndTheirBranchIds: typeof getCommitsAndTheirBranchIds + } & GetObjectResourceGroupsDeps + ): GetViewerResourcesFromLegacyIdentifiers => + async ( + projectId: string, + resources: Array + ): Promise => { + if (!resources.length || !projectId) return [] - const objectIds = resources - .filter((r) => r.resourceType === ResourceType.Object) - .map((r) => r.resourceId) - const commitIds = resources - .filter((r) => r.resourceType === ResourceType.Commit) - .map((r) => r.resourceId) - const commentIds = resources - .filter((r) => r.resourceType === ResourceType.Comment) - .map((r) => r.resourceId) + const objectIds = resources + .filter((r) => r.resourceType === ResourceType.Object) + .map((r) => r.resourceId) + const commitIds = resources + .filter((r) => r.resourceType === ResourceType.Commit) + .map((r) => r.resourceId) + const commentIds = resources + .filter((r) => r.resourceType === ResourceType.Comment) + .map((r) => r.resourceId) - const objectResourcesBuilder = SpeckleViewer.ViewerRoute.resourceBuilder() - for (const objectId of objectIds) { - objectResourcesBuilder.addObject(objectId) + const objectResourcesBuilder = SpeckleViewer.ViewerRoute.resourceBuilder() + for (const objectId of objectIds) { + objectResourcesBuilder.addObject(objectId) + } + const objectResources = objectResourcesBuilder + .toResources() + .filter(SpeckleViewer.ViewerRoute.isObjectResource) + + const [objectResourceGroups, commitsWithBranchIds, commentResources] = + await Promise.all([ + getObjectResourceGroupsFactory(deps)(projectId, objectResources), + deps.getCommitsAndTheirBranchIds(commitIds), + deps.getViewerResourcesForComments(projectId, commentIds) // recursively getting parent comment resources + ]) + + let results: ViewerResourceItem[] = [] + for (const group of objectResourceGroups) { + results = results.concat(group.items) + } + for (const commit of commitsWithBranchIds) { + results.push({ + objectId: commit.referencedObject, + versionId: commit.id, + modelId: commit.branchId + }) + } + results = results.concat(commentResources) + + return uniqWith(results, isResourceItemEqual) } - const objectResources = objectResourcesBuilder - .toResources() - .filter(SpeckleViewer.ViewerRoute.isObjectResource) - const [objectResourceGroups, commitsWithBranchIds, commentResources] = - await Promise.all([ - getObjectResourceGroups(projectId, objectResources), - getCommitsAndTheirBranchIds(commitIds), - getViewerResourcesForComments(projectId, commentIds) // recursively getting parent comment resources - ]) - - let results: ViewerResourceItem[] = [] - for (const group of objectResourceGroups) { - results = results.concat(group.items) - } - for (const commit of commitsWithBranchIds) { - results.push({ - objectId: commit.referencedObject, - versionId: commit.id, - modelId: commit.branchId - }) - } - results = results.concat(commentResources) - - return uniqWith(results, isResourceItemEqual) +type GetViewerResourcesForCommentsDeps = { + getCommentsResources: GetCommentsResources + getViewerResourcesFromLegacyIdentifiers: GetViewerResourcesFromLegacyIdentifiers } -export async function getViewerResourcesForComments( - projectId: string, - commentIds: string[] -): Promise { - const commentsResources = reduce( - await getCommentsResources(commentIds), - (result, item) => { - const resources = item.resources - return result.concat(resources) - }, - [] as ResourceIdentifier[] - ) - const uniqueResources = uniqWith(commentsResources, isResourceIdentifierEqual) +export const getViewerResourcesForCommentsFactory = + (deps: GetViewerResourcesForCommentsDeps): GetViewerResourcesForComments => + async (projectId: string, commentIds: string[]): Promise => { + const commentsResources = reduce( + await deps.getCommentsResources(commentIds), + (result, item) => { + const resources = item.resources + return result.concat(resources) + }, + [] as ResourceIdentifier[] + ) + const uniqueResources = uniqWith(commentsResources, isResourceIdentifierEqual) - return await getViewerResourcesFromLegacyIdentifiers(projectId, uniqueResources) -} + return await deps.getViewerResourcesFromLegacyIdentifiers( + projectId, + uniqueResources + ) + } -export async function getViewerResourcesForComment( - projectId: string, - commentId: string -): Promise { - return await getViewerResourcesForComments(projectId, [commentId]) -} +export const getViewerResourcesForCommentFactory = + (deps: GetViewerResourcesForCommentsDeps): GetViewerResourcesForComment => + async (projectId: string, commentId: string): Promise => { + return await getViewerResourcesForCommentsFactory(deps)(projectId, [commentId]) + } /** * Whether any of the resource items match