chore(server): core IoC #68 - getAdminUsersListCollectionFactory
This commit is contained in:
@@ -175,3 +175,28 @@ export type AdminUserList = (args: AdminUserListArgs) => Promise<{
|
||||
items: UserWithRole[]
|
||||
cursor: string | null
|
||||
}>
|
||||
|
||||
export type LegacyAdminUsersPaginationParams = {
|
||||
limit: number
|
||||
offset: number
|
||||
query: string | null
|
||||
}
|
||||
|
||||
export type LegacyAdminUsersListItem = {
|
||||
registeredUser: Nullable<User>
|
||||
invitedUser: Nullable<{
|
||||
id: string
|
||||
email: string
|
||||
invitedById: string
|
||||
}>
|
||||
id: string
|
||||
}
|
||||
|
||||
type LegacyAdminUsersListCollection = {
|
||||
totalCount: number
|
||||
items: Array<LegacyAdminUsersListItem>
|
||||
}
|
||||
|
||||
export type LegacyGetAdminUsersListCollection = (
|
||||
params: LegacyAdminUsersPaginationParams
|
||||
) => Promise<LegacyAdminUsersListCollection>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
const { ActionTypes } = require('@/modules/activitystream/helpers/types')
|
||||
const { validateScopes } = require(`@/modules/shared`)
|
||||
const zxcvbn = require('zxcvbn')
|
||||
const {
|
||||
getAdminUsersListCollection,
|
||||
getTotalCounts
|
||||
} = require('@/modules/core/services/users/adminUsersListService')
|
||||
const { Roles, Scopes } = require('@speckle/shared')
|
||||
const {
|
||||
legacyGetUserFactory,
|
||||
@@ -16,7 +12,9 @@ const {
|
||||
getUserRoleFactory,
|
||||
updateUserServerRoleFactory,
|
||||
searchUsersFactory,
|
||||
markOnboardingCompleteFactory
|
||||
markOnboardingCompleteFactory,
|
||||
legacyGetPaginatedUsersCountFactory,
|
||||
legacyGetPaginatedUsersFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const { UsersMeta } = require('@/modules/core/dbSchema')
|
||||
const { getServerInfo } = require('@/modules/core/services/generic')
|
||||
@@ -42,6 +40,9 @@ const {
|
||||
getUserDeletableStreamsFactory
|
||||
} = require('@/modules/core/repositories/streams')
|
||||
const { dbLogger } = require('@/logging/logging')
|
||||
const {
|
||||
getAdminUsersListCollectionFactory
|
||||
} = require('@/modules/core/services/users/legacyAdminUsersList')
|
||||
|
||||
const getUser = legacyGetUserFactory({ db })
|
||||
const getUserByEmail = legacyGetUserByEmailFactory({ db })
|
||||
@@ -70,6 +71,12 @@ const changeUserRole = changeUserRoleFactory({
|
||||
})
|
||||
const searchUsers = searchUsersFactory({ db })
|
||||
const markOnboardingComplete = markOnboardingCompleteFactory({ db })
|
||||
const getAdminUsersListCollection = getAdminUsersListCollectionFactory({
|
||||
countUsers: legacyGetPaginatedUsersCountFactory({ db }),
|
||||
countServerInvites: countServerInvitesFactory({ db }),
|
||||
findServerInvites: findServerInvitesFactory({ db }),
|
||||
getUsers: legacyGetPaginatedUsersFactory({ db })
|
||||
})
|
||||
|
||||
/** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */
|
||||
module.exports = {
|
||||
@@ -109,12 +116,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
async adminUsers(_parent, args) {
|
||||
return await getAdminUsersListCollection({
|
||||
findServerInvites: findServerInvitesFactory({ db }),
|
||||
getTotalCounts: getTotalCounts({
|
||||
countServerInvites: countServerInvitesFactory({ db })
|
||||
})
|
||||
})(args)
|
||||
return await getAdminUsersListCollection(args)
|
||||
},
|
||||
|
||||
async userSearch(parent, args, context) {
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
const { db } = require('@/db/knex')
|
||||
const {
|
||||
legacyGetPaginatedUsersCountFactory,
|
||||
legacyGetPaginatedUsersFactory
|
||||
} = require('@/modules/core/repositories/users')
|
||||
const { resolveTarget } = require('@/modules/serverinvites/helpers/core')
|
||||
const { clamp } = require('lodash')
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* id: string,
|
||||
* email: string,
|
||||
* invitedById: string
|
||||
* }} ServerInviteGraphqlReturnType
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* registeredUser: import("@/modules/core/helpers/userHelper").UserRecord | null,
|
||||
* invitedUser: ServerInviteGraphqlReturnType | null,
|
||||
* id: string
|
||||
* }} AdminUsersListItem
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* totalCount: number,
|
||||
* items: Array<AdminUsersListItem>
|
||||
* }} AdminUsersListCollection
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* limit: number,
|
||||
* offset: number,
|
||||
* query: string | null
|
||||
* }} PaginationParams
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* userCount: number,
|
||||
* inviteCount: number,
|
||||
* totalCount: number
|
||||
* }} TotalCounts
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* invitesFilter: {offset: number, limit: number} | null,
|
||||
* usersFilter: {offset: number, limit: number} | null
|
||||
* }} UsersInvitesFilters
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sanitizing params to ensure limits aren't too high etc.
|
||||
* @param {PaginationParams} params
|
||||
* @returns {PaginationParams}
|
||||
*/
|
||||
function sanitizeParams(params) {
|
||||
params.limit = clamp(params.limit || 10, 1, 200)
|
||||
params.offset = Math.max(params.offset || 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total users & invites that we can find using these params
|
||||
* @param {{ countServerInvites: import('@/modules/serverinvites/domain/operations').CountServerInvites}} param0
|
||||
*/
|
||||
function getTotalCounts({ countServerInvites }) {
|
||||
const countUsers = legacyGetPaginatedUsersCountFactory({ db })
|
||||
|
||||
/**
|
||||
* Get total users & invites that we can find using these params
|
||||
* @param {PaginationParams} params
|
||||
* @returns {Promise<TotalCounts>}
|
||||
*/
|
||||
return async (params) => {
|
||||
const { query } = params
|
||||
|
||||
const [userCount, inviteCount] = await Promise.all([
|
||||
// Actual users
|
||||
countUsers(query),
|
||||
// Invites
|
||||
countServerInvites(query)
|
||||
])
|
||||
const totalCount = userCount + inviteCount
|
||||
|
||||
return { userCount, inviteCount, totalCount }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve limits & offsets for user & invite queries. All invites will always appear first,
|
||||
* and only once there are no more results will users start appearing in the list
|
||||
* @param {PaginationParams} params
|
||||
* @param {TotalCounts} totalCounts
|
||||
* @returns {UsersInvitesFilters}
|
||||
*/
|
||||
function resolveLimitsAndOffsets(params, totalCounts) {
|
||||
const { offset, limit } = params
|
||||
const { inviteCount } = totalCounts
|
||||
|
||||
const inviteOffset = offset < inviteCount ? offset : null
|
||||
const inviteLimit =
|
||||
inviteOffset !== null ? Math.min(inviteCount - offset, limit) : null
|
||||
|
||||
const userOffset = inviteOffset !== null ? 0 : offset - inviteCount
|
||||
const userLimit = limit - (inviteLimit || 0)
|
||||
|
||||
return {
|
||||
invitesFilter: inviteLimit ? { limit: inviteLimit, offset: inviteOffset } : null,
|
||||
usersFilter: userLimit ? { limit: userLimit, offset: userOffset } : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@/modules/core/helpers/userHelper').UserRecord} user
|
||||
* @returns {AdminUsersListItem}
|
||||
*/
|
||||
function mapUserToListItem(user) {
|
||||
return {
|
||||
invitedUser: null,
|
||||
registeredUser: user,
|
||||
id: `user:${user.id}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@/modules/serverinvites/domain/types').ServerInviteRecord} invite
|
||||
* @returns {AdminUsersListItem}
|
||||
*/
|
||||
function mapInviteToListItem(invite) {
|
||||
return {
|
||||
registeredUser: null,
|
||||
invitedUser: {
|
||||
id: invite.id,
|
||||
invitedById: invite.inviterId,
|
||||
email: resolveTarget(invite.target).userEmail
|
||||
},
|
||||
id: `invite:${invite.id}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {{findServerInvites: import('@/modules/serverinvites/domain/operations').FindServerInvites}} param0
|
||||
*/
|
||||
function retrieveItems({ findServerInvites }) {
|
||||
const getUsers = legacyGetPaginatedUsersFactory({ db })
|
||||
|
||||
/**
|
||||
* Retrieve all list items from DB and convert them to the target model
|
||||
* @param {PaginationParams} params
|
||||
* @param {TotalCounts} counts
|
||||
* @returns {Promise<AdminUsersListItem[]>}
|
||||
*/
|
||||
return async (params, counts) => {
|
||||
const { invitesFilter, usersFilter } = resolveLimitsAndOffsets(params, counts)
|
||||
const { query } = params
|
||||
|
||||
const [invites, users] = await Promise.all([
|
||||
// Invites
|
||||
invitesFilter
|
||||
? findServerInvites(query, invitesFilter.limit, invitesFilter.offset)
|
||||
: [],
|
||||
// Users
|
||||
usersFilter ? getUsers(usersFilter.limit, usersFilter.offset, query) : []
|
||||
])
|
||||
|
||||
return [
|
||||
// Invites first
|
||||
...invites.map((i) => mapInviteToListItem(i)),
|
||||
// Users after
|
||||
...users.map((u) => mapUserToListItem(u))
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {{ getTotalCounts: (params: PaginationParams) => Promise<number>, findServerInvites: import('@/modules/serverinvites/domain/operations').FindServerInvites}} param0
|
||||
*/
|
||||
function getAdminUsersListCollection({ getTotalCounts, findServerInvites }) {
|
||||
/**
|
||||
* Resolve admin users list data using the specified filter params
|
||||
* @param {PaginationParams} params
|
||||
* @returns {Promise<AdminUsersListCollection>}
|
||||
*/
|
||||
return async (params) => {
|
||||
sanitizeParams(params)
|
||||
|
||||
const totalCounts = await getTotalCounts(params)
|
||||
const items = await retrieveItems({ findServerInvites })(params, totalCounts)
|
||||
|
||||
return {
|
||||
items,
|
||||
totalCount: totalCounts.totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAdminUsersListCollection,
|
||||
getTotalCounts
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import {
|
||||
LegacyAdminUsersListItem,
|
||||
LegacyAdminUsersPaginationParams,
|
||||
LegacyGetAdminUsersListCollection,
|
||||
LegacyGetPaginatedUsers,
|
||||
LegacyGetPaginatedUsersCount
|
||||
} from '@/modules/core/domain/users/operations'
|
||||
import { UserRecord } from '@/modules/core/helpers/userHelper'
|
||||
import {
|
||||
CountServerInvites,
|
||||
FindServerInvites
|
||||
} from '@/modules/serverinvites/domain/operations'
|
||||
import { ServerInviteRecord } from '@/modules/serverinvites/domain/types'
|
||||
import { resolveTarget } from '@/modules/serverinvites/helpers/core'
|
||||
import { clamp } from 'lodash'
|
||||
|
||||
type LegacyGetUsersInvitesTotalCounts = {
|
||||
userCount: number
|
||||
inviteCount: number
|
||||
totalCount: number
|
||||
}
|
||||
|
||||
type GetTotalCountsDeps = {
|
||||
countUsers: LegacyGetPaginatedUsersCount
|
||||
countServerInvites: CountServerInvites
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total users & invites that we can find using these params
|
||||
*/
|
||||
const getTotalCountsFactory =
|
||||
(deps: GetTotalCountsDeps) =>
|
||||
async (
|
||||
params: LegacyAdminUsersPaginationParams
|
||||
): Promise<LegacyGetUsersInvitesTotalCounts> => {
|
||||
const { query } = params
|
||||
|
||||
const [userCount, inviteCount] = await Promise.all([
|
||||
// Actual users
|
||||
deps.countUsers(query),
|
||||
// Invites
|
||||
deps.countServerInvites(query)
|
||||
])
|
||||
const totalCount = userCount + inviteCount
|
||||
|
||||
return { userCount, inviteCount, totalCount }
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizing params to ensure limits aren't too high etc.
|
||||
*/
|
||||
function sanitizeParams(params: LegacyAdminUsersPaginationParams) {
|
||||
params.limit = clamp(params.limit || 10, 1, 200)
|
||||
params.offset = Math.max(params.offset || 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve limits & offsets for user & invite queries. All invites will always appear first,
|
||||
* and only once there are no more results will users start appearing in the list
|
||||
*/
|
||||
function resolveLimitsAndOffsets(
|
||||
params: LegacyAdminUsersPaginationParams,
|
||||
totalCounts: LegacyGetUsersInvitesTotalCounts
|
||||
) {
|
||||
const { offset, limit } = params
|
||||
const { inviteCount } = totalCounts
|
||||
|
||||
const inviteOffset = offset < inviteCount ? offset : null
|
||||
const inviteLimit =
|
||||
inviteOffset !== null ? Math.min(inviteCount - offset, limit) : null
|
||||
|
||||
const userOffset = inviteOffset !== null ? 0 : offset - inviteCount
|
||||
const userLimit = limit - (inviteLimit || 0)
|
||||
|
||||
return {
|
||||
invitesFilter: inviteLimit ? { limit: inviteLimit, offset: inviteOffset } : null,
|
||||
usersFilter: userLimit ? { limit: userLimit, offset: userOffset } : null
|
||||
}
|
||||
}
|
||||
|
||||
function mapUserToListItem(user: UserRecord): LegacyAdminUsersListItem {
|
||||
return {
|
||||
invitedUser: null,
|
||||
registeredUser: user,
|
||||
id: `user:${user.id}`
|
||||
}
|
||||
}
|
||||
|
||||
function mapInviteToListItem(invite: ServerInviteRecord): LegacyAdminUsersListItem {
|
||||
return {
|
||||
registeredUser: null,
|
||||
invitedUser: {
|
||||
id: invite.id,
|
||||
invitedById: invite.inviterId,
|
||||
email: resolveTarget(invite.target).userEmail!
|
||||
},
|
||||
id: `invite:${invite.id}`
|
||||
}
|
||||
}
|
||||
|
||||
type RetrieveItemsDeps = {
|
||||
findServerInvites: FindServerInvites
|
||||
getUsers: LegacyGetPaginatedUsers
|
||||
}
|
||||
|
||||
const retrieveItemsFactory =
|
||||
(deps: RetrieveItemsDeps) =>
|
||||
async (
|
||||
params: LegacyAdminUsersPaginationParams,
|
||||
counts: LegacyGetUsersInvitesTotalCounts
|
||||
) => {
|
||||
const { invitesFilter, usersFilter } = resolveLimitsAndOffsets(params, counts)
|
||||
const { query } = params
|
||||
|
||||
const [invites, users] = await Promise.all([
|
||||
// Invites
|
||||
invitesFilter
|
||||
? deps.findServerInvites(query, invitesFilter.limit, invitesFilter.offset || 0)
|
||||
: [],
|
||||
// Users
|
||||
usersFilter ? deps.getUsers(usersFilter.limit, usersFilter.offset, query) : []
|
||||
])
|
||||
|
||||
return [
|
||||
// Invites first
|
||||
...invites.map((i) => mapInviteToListItem(i)),
|
||||
// Users after
|
||||
...users.map((u) => mapUserToListItem(u))
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve admin users list data using the specified filter params
|
||||
*/
|
||||
export const getAdminUsersListCollectionFactory =
|
||||
(deps: GetTotalCountsDeps & RetrieveItemsDeps): LegacyGetAdminUsersListCollection =>
|
||||
async (params) => {
|
||||
sanitizeParams(params)
|
||||
|
||||
const totalCounts = await getTotalCountsFactory(deps)(params)
|
||||
const items = await retrieveItemsFactory(deps)(params, totalCounts)
|
||||
|
||||
return {
|
||||
items,
|
||||
totalCount: totalCounts.totalCount
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user