Merge pull request #3280 from specklesystems/fabians/core-ioc-57
chore(server): core IoC #57 - getUserByEmailFactory
This commit is contained in:
@@ -24,6 +24,14 @@ export type GetUser = (
|
||||
params?: GetUserParams
|
||||
) => Promise<Nullable<UserWithOptionalRole>>
|
||||
|
||||
export type GetUserByEmail = (
|
||||
email: string,
|
||||
options?: Partial<{
|
||||
skipClean: boolean
|
||||
withRole: boolean
|
||||
}>
|
||||
) => Promise<UserWithOptionalRole | null>
|
||||
|
||||
export type StoreUser = (params: {
|
||||
user: Omit<NullableKeysToOptional<User>, 'suuid' | 'createdAt'>
|
||||
}) => Promise<User>
|
||||
|
||||
@@ -13,6 +13,7 @@ import { UserWithOptionalRole } from '@/modules/core/domain/users/types'
|
||||
import {
|
||||
CountAdminUsers,
|
||||
GetUser,
|
||||
GetUserByEmail,
|
||||
GetUserParams,
|
||||
GetUsers,
|
||||
LegacyGetPaginatedUsers,
|
||||
@@ -154,31 +155,35 @@ export const getUserFactory =
|
||||
/**
|
||||
* Get user by e-mail address
|
||||
*/
|
||||
export async function getUserByEmail(
|
||||
email: string,
|
||||
options?: Partial<{ skipClean: boolean; withRole: boolean }>
|
||||
) {
|
||||
const q = Users.knex<UserWithOptionalRole[]>()
|
||||
.leftJoin(UserEmails.name, UserEmails.col.userId, Users.col.id)
|
||||
.where({
|
||||
[UserEmails.col.primary]: true
|
||||
})
|
||||
.whereRaw('lower("user_emails"."email") = lower(?)', [email])
|
||||
const columns: (Knex.Raw<UserRecord> | string)[] = [
|
||||
...Object.values(omit(Users.col, ['email', 'verified'])),
|
||||
knex.raw(`(array_agg("user_emails"."email"))[1] as email`),
|
||||
knex.raw(`(array_agg("user_emails"."verified"))[1] as verified`)
|
||||
]
|
||||
if (options?.withRole) {
|
||||
// Getting first role from grouped results
|
||||
columns.push(knex.raw(`(array_agg("server_acl"."role"))[1] as role`))
|
||||
q.leftJoin(ServerAcl.name, ServerAcl.col.userId, Users.col.id)
|
||||
export const getUserByEmailFactory =
|
||||
(deps: { db: Knex }): GetUserByEmail =>
|
||||
async (
|
||||
email: string,
|
||||
options?: Partial<{ skipClean: boolean; withRole: boolean }>
|
||||
) => {
|
||||
const q = tables
|
||||
.users(deps.db)
|
||||
.leftJoin(UserEmails.name, UserEmails.col.userId, Users.col.id)
|
||||
.where({
|
||||
[UserEmails.col.primary]: true
|
||||
})
|
||||
.whereRaw('lower("user_emails"."email") = lower(?)', [email])
|
||||
const columns: (Knex.Raw<UserRecord> | string)[] = [
|
||||
...Object.values(omit(Users.col, ['email', 'verified'])),
|
||||
knex.raw(`(array_agg("user_emails"."email"))[1] as email`),
|
||||
knex.raw(`(array_agg("user_emails"."verified"))[1] as verified`)
|
||||
]
|
||||
if (options?.withRole) {
|
||||
// Getting first role from grouped results
|
||||
columns.push(knex.raw(`(array_agg("server_acl"."role"))[1] as role`))
|
||||
q.leftJoin(ServerAcl.name, ServerAcl.col.userId, Users.col.id)
|
||||
}
|
||||
q.columns(columns)
|
||||
q.groupBy(Users.col.id)
|
||||
|
||||
const user = (await q.first()) as UserWithOptionalRole
|
||||
return user ? (!options?.skipClean ? sanitizeUserRecord(user) : user) : null
|
||||
}
|
||||
q.columns(columns)
|
||||
q.groupBy(Users.col.id)
|
||||
const user = await q.first()
|
||||
return user ? (!options?.skipClean ? sanitizeUserRecord(user) : user) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a user as verified by e-mail address, and return true on success
|
||||
|
||||
@@ -16,7 +16,6 @@ const Users = () => UsersSchema.knex()
|
||||
const Acl = () => ServerAclSchema.knex()
|
||||
|
||||
const { LIMITED_USER_FIELDS } = require('@/modules/core/helpers/userHelper')
|
||||
const { getUserByEmail } = require('@/modules/core/repositories/users')
|
||||
const { omit } = require('lodash')
|
||||
const { dbLogger } = require('@/logging/logging')
|
||||
const {
|
||||
@@ -26,6 +25,7 @@ const {
|
||||
const { Roles } = require('@speckle/shared')
|
||||
const { db } = require('@/db/knex')
|
||||
const { deleteStreamFactory } = require('@/modules/core/repositories/streams')
|
||||
const { getUserByEmailFactory } = require('@/modules/core/repositories/users')
|
||||
|
||||
const _changeUserRole = async ({ userId, role }) =>
|
||||
await Acl().where({ userId }).update({ role })
|
||||
@@ -42,6 +42,7 @@ const _ensureAtleastOneAdminRemains = async (userId) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
const getUserByEmail = getUserByEmailFactory({ db })
|
||||
|
||||
module.exports = {
|
||||
// TODO: this should be moved to repository
|
||||
|
||||
@@ -13,7 +13,7 @@ import { db } from '@/db/knex'
|
||||
import { expect } from 'chai'
|
||||
import {
|
||||
countAdminUsersFactory,
|
||||
getUserByEmail,
|
||||
getUserByEmailFactory,
|
||||
getUserFactory,
|
||||
getUsersFactory,
|
||||
listUsers,
|
||||
@@ -62,6 +62,7 @@ const createUser = createUserFactory({
|
||||
}),
|
||||
usersEventsEmitter: UsersEmitter.emit
|
||||
})
|
||||
const getUserByEmail = getUserByEmailFactory({ db })
|
||||
|
||||
describe('Find users @core', () => {
|
||||
describe('getUsers', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { beforeEachContext } from '@/test/hooks'
|
||||
import { expect } from 'chai'
|
||||
import {
|
||||
countAdminUsersFactory,
|
||||
getUserByEmail,
|
||||
getUserByEmailFactory,
|
||||
getUserFactory,
|
||||
legacyGetPaginatedUsersCount,
|
||||
legacyGetPaginatedUsersFactory,
|
||||
@@ -81,6 +81,7 @@ const createUser = createUserFactory({
|
||||
validateAndCreateUserEmail: createUserEmail,
|
||||
usersEventsEmitter: UsersEmitter.emit
|
||||
})
|
||||
const getUserByEmail = getUserByEmailFactory({ db })
|
||||
|
||||
describe('Core @user-emails', () => {
|
||||
before(async () => {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { db } from '@/db/knex'
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
import { findPrimaryEmailForUserFactory } from '@/modules/core/repositories/userEmails'
|
||||
import { getUserByEmail, getUserFactory } from '@/modules/core/repositories/users'
|
||||
import {
|
||||
getUserByEmailFactory,
|
||||
getUserFactory
|
||||
} from '@/modules/core/repositories/users'
|
||||
import { getServerInfo } from '@/modules/core/services/generic'
|
||||
import {
|
||||
deleteOldAndInsertNewVerificationFactory,
|
||||
@@ -20,6 +23,7 @@ const requestEmailVerification = requestEmailVerificationFactory({
|
||||
sendEmail,
|
||||
renderEmail
|
||||
})
|
||||
const getUserByEmail = getUserByEmailFactory({ db })
|
||||
|
||||
export = {
|
||||
User: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { db } from '@/db/knex'
|
||||
import { deleteExistingAuthTokensFactory } from '@/modules/auth/repositories'
|
||||
import { getUserByEmail } from '@/modules/core/repositories/users'
|
||||
import { getUserByEmailFactory } from '@/modules/core/repositories/users'
|
||||
import { getServerInfo } from '@/modules/core/services/generic'
|
||||
import { updateUserPassword } from '@/modules/core/services/users'
|
||||
import { renderEmail } from '@/modules/emails/services/emailRendering'
|
||||
@@ -16,6 +16,8 @@ import { ensureError } from '@/modules/shared/helpers/errorHelper'
|
||||
import { Express } from 'express'
|
||||
|
||||
export default function (app: Express) {
|
||||
const getUserByEmail = getUserByEmailFactory({ db })
|
||||
|
||||
// sends a password recovery email.
|
||||
app.post('/auth/pwdreset/request', async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { DeleteExistingUserAuthTokens } from '@/modules/auth/domain/operations'
|
||||
import { getUserByEmail } from '@/modules/core/repositories/users'
|
||||
import { GetUserByEmail } from '@/modules/core/domain/users/operations'
|
||||
import { updateUserPassword } from '@/modules/core/services/users'
|
||||
import { DeleteTokens, GetPendingToken } from '@/modules/pwdreset/domain/operations'
|
||||
import { PasswordRecoveryFinalizationError } from '@/modules/pwdreset/errors'
|
||||
|
||||
type InitializeStateDeps = {
|
||||
getUserByEmail: typeof getUserByEmail
|
||||
getUserByEmail: GetUserByEmail
|
||||
getPendingToken: GetPendingToken
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GetUserByEmail } from '@/modules/core/domain/users/operations'
|
||||
import { getPasswordResetFinalizationRoute } from '@/modules/core/helpers/routeHelper'
|
||||
import { getUserByEmail } from '@/modules/core/repositories/users'
|
||||
import { getServerInfo } from '@/modules/core/services/generic'
|
||||
import {
|
||||
EmailTemplateParams,
|
||||
@@ -14,7 +14,7 @@ import { getFrontendOrigin } from '@/modules/shared/helpers/envHelper'
|
||||
const EMAIL_SUBJECT = 'Speckle Account Password Reset'
|
||||
|
||||
type InitializeNewTokenDeps = {
|
||||
getUserByEmail: typeof getUserByEmail
|
||||
getUserByEmail: GetUserByEmail
|
||||
getPendingToken: GetPendingToken
|
||||
createToken: CreateToken
|
||||
getServerInfo: typeof getServerInfo
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { knex, ServerInvites, Streams, Users } from '@/modules/core/dbSchema'
|
||||
import {
|
||||
getUserByEmail,
|
||||
getUserByEmailFactory,
|
||||
getUserFactory,
|
||||
UserWithOptionalRole
|
||||
} from '@/modules/core/repositories/users'
|
||||
@@ -131,7 +131,7 @@ export const findUserByTargetFactory =
|
||||
(target: string): Promise<UserWithOptionalRole | null> => {
|
||||
const { userEmail, userId } = resolveTarget(target)
|
||||
return userEmail
|
||||
? getUserByEmail(userEmail, { withRole: true })
|
||||
? getUserByEmailFactory(deps)(userEmail, { withRole: true })
|
||||
: getUserFactory(deps)(userId!, { withRole: true })
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user