chore(activitystream): refactor getUserActivity

This commit is contained in:
Alessandro Magionami
2024-09-25 15:58:49 +02:00
parent 66d215371a
commit c3abd854ff
6 changed files with 136 additions and 105 deletions
@@ -114,3 +114,22 @@ export type GetResourceActivity = ({
cursor: string | null
items: StreamActivityRecord[]
}>
export type GetUserActivity = ({
userId,
actionType,
before,
after,
cursor,
limit
}: {
userId: string
actionType: StreamActionType
after?: Date
before?: Date
cursor?: Date
limit?: number
}) => Promise<{
cursor: string | null
items: StreamActivityRecord[]
}>
@@ -1,84 +0,0 @@
'use strict'
const { md5 } = require('@/modules/shared/helpers/cryptoHelper')
const { getUserActivity } = require('../../services/index')
const {
getActivityCountByUserIdFactory,
getTimelineCountFactory,
getUserTimelineFactory
} = require('@/modules/activitystream/repositories')
const { db } = require('@/db/knex')
const userActivityQueryCore = async (parent, args) => {
const { items, cursor } = await getUserActivity({
userId: parent.id,
actionType: args.actionType,
after: args.after,
before: args.before,
cursor: args.cursor,
limit: args.limit
})
const totalCount = await getActivityCountByUserIdFactory({ db })({
userId: parent.id,
actionType: args.actionType,
after: args.after,
before: args.before
})
return { items, cursor, totalCount }
}
const userTimelineQueryCore = async (parent, args) => {
const { items, cursor } = await getUserTimelineFactory({ db })({
userId: parent.id,
after: args.after,
before: args.before,
cursor: args.cursor,
limit: args.limit
})
const totalCount = await getTimelineCountFactory({ db })({
userId: parent.id,
after: args.after,
before: args.before
})
return { items, cursor, totalCount }
}
/** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */
module.exports = {
LimitedUser: {
async activity(parent, args) {
return await userActivityQueryCore(parent, args)
},
async timeline(parent, args) {
return await userTimelineQueryCore(parent, args)
}
},
User: {
async activity(parent, args) {
return await userActivityQueryCore(parent, args)
},
async timeline(parent, args) {
return await userTimelineQueryCore(parent, args)
}
},
Activity: {
/**
* We need a unique ID to be able to properly cache stuff on the clientside
*/
id(parent) {
if (!parent) return null
const { streamId, resourceId, userId, time } = parent
const plainIdentity = JSON.stringify({
streamId,
resourceId,
userId,
time
})
return md5(plainIdentity)
}
}
}
@@ -3,14 +3,87 @@ import { ActionTypes } from '@/modules/activitystream/helpers/types'
import {
getActivityCountByResourceIdFactory,
getActivityCountByStreamIdFactory,
getActivityCountByUserIdFactory,
getResourceActivityFactory,
getStreamActivityFactory
getStreamActivityFactory,
getTimelineCountFactory,
getUserActivityFactory,
getUserTimelineFactory
} from '@/modules/activitystream/repositories'
import { Resolvers } from '@/modules/core/graph/generated/graphql'
import { InvalidActionTypeError } from '@/modules/activitystream/errors/activityStream'
import { StreamActionType } from '@/modules/activitystream/domain/types'
import { md5 } from '@/modules/shared/helpers/cryptoHelper'
type ActivityPaginatedArgs = {
actionType?: string | null
after?: Date | null
before?: Date | null
cursor?: Date | null
limit?: number // This field is required because the type-defs defines it as required
}
const userActivityQueryCore = async (
parent: { id: string },
args: ActivityPaginatedArgs
) => {
const { items, cursor } = await getUserActivityFactory({ db })({
userId: parent.id,
actionType: (args.actionType as StreamActionType) ?? undefined,
after: args.after ?? undefined,
before: args.before ?? undefined,
cursor: args.cursor ?? undefined,
limit: args.limit
})
const totalCount = await getActivityCountByUserIdFactory({ db })({
userId: parent.id,
actionType: (args.actionType as StreamActionType) ?? undefined,
after: args.after ?? undefined,
before: args.before ?? undefined
})
return { items, cursor, totalCount }
}
const userTimelineQueryCore = async (
parent: { id: string },
args: ActivityPaginatedArgs
) => {
const { items, cursor } = await getUserTimelineFactory({ db })({
userId: parent.id,
after: args.after ?? undefined,
before: args.before ?? undefined,
cursor: args.cursor ?? undefined,
limit: args.limit
})
const totalCount = await getTimelineCountFactory({ db })({
userId: parent.id,
after: args.after ?? undefined,
before: args.before ?? undefined
})
return { items, cursor, totalCount }
}
export = {
LimitedUser: {
async activity(parent, args) {
return await userActivityQueryCore(parent, args)
},
async timeline(parent, args) {
return await userTimelineQueryCore(parent, args)
}
},
User: {
async activity(parent, args) {
return await userActivityQueryCore(parent, args)
},
async timeline(parent, args) {
return await userTimelineQueryCore(parent, args)
}
},
Stream: {
async activity(parent, args) {
if (
@@ -79,5 +152,22 @@ export = {
return { items, cursor, totalCount }
}
},
Activity: {
/**
* We need a unique ID to be able to properly cache stuff on the clientside
*/
id(parent) {
if (!parent) return null
const { streamId, resourceId, userId, time } = parent
const plainIdentity = JSON.stringify({
streamId,
resourceId,
userId,
time
})
return md5(plainIdentity)
}
}
} as Resolvers
@@ -7,6 +7,7 @@ import {
GetResourceActivity,
GetStreamActivity,
GetTimelineCount,
GetUserActivity,
GetUserTimeline
} from '@/modules/activitystream/domain/operations'
import {
@@ -191,3 +192,24 @@ export const getResourceActivityFactory =
cursor: results.length > 0 ? results[results.length - 1].time.toISOString() : null
}
}
export const getUserActivityFactory =
({ db }: { db: Knex }): GetUserActivity =>
async ({ userId, actionType, after, before, cursor, limit }) => {
if (!limit) {
limit = 200
}
const dbQuery = tables.streamActivity(db).where({ userId })
if (actionType) dbQuery.andWhere({ actionType })
if (after) dbQuery.andWhere('time', '>', after)
if (before) dbQuery.andWhere('time', '<', before)
if (cursor) dbQuery.andWhere('time', '<', cursor)
dbQuery.orderBy('time', 'desc').limit(limit)
const results = await dbQuery.select('*')
return {
items: results,
cursor: results.length > 0 ? results[results.length - 1].time.toISOString() : null
}
}
@@ -61,24 +61,5 @@ module.exports = {
{ trx }
)
}
},
async getUserActivity({ userId, actionType, after, before, cursor, limit }) {
if (!limit) {
limit = 200
}
const dbQuery = StreamActivity().where({ userId })
if (actionType) dbQuery.andWhere({ actionType })
if (after) dbQuery.andWhere('time', '>', after)
if (before) dbQuery.andWhere('time', '<', before)
if (cursor) dbQuery.andWhere('time', '<', cursor)
dbQuery.orderBy('time', 'desc').limit(limit)
const results = await dbQuery.select('*')
return {
items: results,
cursor: results.length > 0 ? results[results.length - 1].time.toISOString() : null
}
}
}
@@ -4,7 +4,6 @@ const expect = require('chai').expect
const { createUser } = require('../../core/services/users')
const { createPersonalAccessToken } = require('../../core/services/tokens')
const { createObject } = require('../../core/services/objects')
const { getUserActivity } = require('../services')
const { beforeEachContext, initializeTestServer } = require('@/test/hooks')
const { noErrors } = require('@/test/helpers')
@@ -12,6 +11,10 @@ const {
addOrUpdateStreamCollaborator
} = require('@/modules/core/services/streams/streamAccessService')
const { Roles, Scopes } = require('@speckle/shared')
const { getUserActivityFactory } = require('@/modules/activitystream/repositories')
const { db } = require('@/db/knex')
const getUserActivity = getUserActivityFactory({ db })
let sendRequest