use findUserByIdentifier for emails, use it in send coins

This commit is contained in:
Moriz Wahl 2023-03-14 21:14:41 +01:00
parent 4c15669c4b
commit 761ff80b97
8 changed files with 99 additions and 54 deletions

View File

@ -4,7 +4,7 @@ import Decimal from 'decimal.js-light'
@ArgsType()
export default class TransactionSendArgs {
@Field(() => String)
email: string
identifier: string
@Field(() => Decimal)
amount: Decimal

View File

@ -24,7 +24,6 @@ import { User } from '@entity/User'
import { cleanDB, testEnvironment } from '@test/helpers'
import { logger } from '@test/testSetup'
import { GraphQLError } from 'graphql'
import { findUserByEmail } from './UserResolver'
let mutate: any, query: any, con: any
let testEnv: any
@ -81,7 +80,7 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'wrong@email.com',
identifier: 'wrong@email.com',
amount: 100,
memo: 'test',
},
@ -109,22 +108,20 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'stephen@hawking.uk',
identifier: 'stephen@hawking.uk',
amount: 100,
memo: 'test',
},
}),
).toEqual(
expect.objectContaining({
errors: [new GraphQLError('The recipient account was deleted')],
errors: [new GraphQLError('No user to given contact')],
}),
)
})
it('logs the error thrown', async () => {
// find peter to check the log
const user = await findUserByEmail('stephen@hawking.uk')
expect(logger.error).toBeCalledWith('The recipient account was deleted', user)
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith('No user to given contact', 'stephen@hawking.uk')
})
})
@ -140,22 +137,23 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'garrick@ollivander.com',
identifier: 'garrick@ollivander.com',
amount: 100,
memo: 'test',
},
}),
).toEqual(
expect.objectContaining({
errors: [new GraphQLError('The recipient account is not activated')],
errors: [new GraphQLError('No user with this credentials')],
}),
)
})
it('logs the error thrown', async () => {
// find peter to check the log
const user = await findUserByEmail('garrick@ollivander.com')
expect(logger.error).toBeCalledWith('The recipient account is not activated', user)
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith(
'No user with this credentials',
'garrick@ollivander.com',
)
})
})
})
@ -175,7 +173,7 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'bob@baumeister.de',
identifier: 'bob@baumeister.de',
amount: 100,
memo: 'test',
},
@ -199,7 +197,7 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 100,
memo: 'test',
},
@ -223,7 +221,7 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 100,
memo: 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test t',
},
@ -247,7 +245,7 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 100,
memo: 'testing',
},
@ -297,7 +295,7 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: -50,
memo: 'testing negative',
},
@ -320,7 +318,7 @@ describe('send coins', () => {
await mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 50,
memo: 'unrepeatable memo',
},
@ -375,7 +373,7 @@ describe('send coins', () => {
mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 10,
memo: 'first transaction',
},
@ -391,7 +389,7 @@ describe('send coins', () => {
mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 20,
memo: 'second transaction',
},
@ -407,7 +405,7 @@ describe('send coins', () => {
mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 30,
memo: 'third transaction',
},
@ -423,7 +421,7 @@ describe('send coins', () => {
mutate({
mutation: sendCoins,
variables: {
email: 'peter@lustig.de',
identifier: 'peter@lustig.de',
amount: 40,
memo: 'fourth transaction',
},

View File

@ -34,7 +34,7 @@ import { EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Event
import { BalanceResolver } from './BalanceResolver'
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
import { findUserByEmail } from './UserResolver'
import { findUserByIdentifier } from './util/findUserByIdentifier'
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
import LogError from '@/server/LogError'
@ -307,10 +307,9 @@ export class TransactionResolver {
@Authorized([RIGHTS.SEND_COINS])
@Mutation(() => Boolean)
async sendCoins(
@Args() { email, amount, memo }: TransactionSendArgs,
@Args() { identifier, amount, memo }: TransactionSendArgs,
@Ctx() context: Context,
): Promise<boolean> {
logger.info(`sendCoins(email=${email}, amount=${amount}, memo=${memo})`)
if (amount.lte(0)) {
throw new LogError('Amount to send must be positive', amount)
}
@ -319,13 +318,9 @@ export class TransactionResolver {
const senderUser = getUser(context)
// validate recipient user
const recipientUser = await findUserByEmail(email)
if (recipientUser.deletedAt) {
throw new LogError('The recipient account was deleted', recipientUser)
}
const emailContact = recipientUser.emailContact
if (!emailContact.emailChecked) {
throw new LogError('The recipient account is not activated', recipientUser)
const recipientUser = await findUserByIdentifier(identifier)
if (!recipientUser) {
throw new LogError('The recipient user was not found', recipientUser)
}
await executeTransaction(amount, memo, senderUser, recipientUser)

View File

@ -2228,8 +2228,8 @@ describe('UserResolver', () => {
})
})
describe('identifier is no gradido ID', () => {
it('throws and logs "No valid gradido ID" error', async () => {
describe('identifier is no gradido ID and no email', () => {
it('throws and logs "Unknown identifier type" error', async () => {
await expect(
query({
query: userQuery,
@ -2239,10 +2239,10 @@ describe('UserResolver', () => {
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('No valid gradido ID')],
errors: [new GraphQLError('Unknown identifier type')],
}),
)
expect(logger.error).toBeCalledWith('No valid gradido ID', 'identifier')
expect(logger.error).toBeCalledWith('Unknown identifier type', 'identifier')
})
})
@ -2267,7 +2267,30 @@ describe('UserResolver', () => {
})
})
describe('identifier is found', () => {
describe('identifier is found via email', () => {
it('returns user', async () => {
await expect(
query({
query: userQuery,
variables: {
identifier: 'bibi@bloxberg.de',
},
}),
).resolves.toEqual(
expect.objectContaining({
data: {
user: {
firstName: 'Bibi',
lastName: 'Bloxberg',
},
},
errors: undefined,
}),
)
})
})
describe('identifier is found via gradidoID', () => {
it('returns user', async () => {
await expect(
query({

View File

@ -68,6 +68,7 @@ import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
import { PasswordEncryptionType } from '../enum/PasswordEncryptionType'
import LogError from '@/server/LogError'
import { EventProtocolType } from '@/event/EventProtocolType'
import { findUserByIdentifier } from './util/findUserByIdentifier'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sodium = require('sodium-native')
@ -815,16 +816,8 @@ export class UserResolver {
@Authorized([RIGHTS.USER])
@Query(() => User, { nullable: true })
async user(@Arg('identifier') identifier: string): Promise<User | null> {
const isGradidoID =
/^[0-9a-f]{8,8}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{12,12}$/.exec(identifier)
if (!isGradidoID) {
throw new LogError('No valid gradido ID', identifier)
}
const user = await DbUser.findOne({ where: { gradidoID: identifier } })
if (!user) {
throw new LogError('No user found to given identifier', identifier)
}
return new User(user)
const user = await findUserByIdentifier(identifier)
return user ? new User(user) : null
}
}

View File

@ -152,7 +152,7 @@ describe('semaphore', () => {
})
const bibisTransaction = mutate({
mutation: sendCoins,
variables: { email: 'bob@baumeister.de', amount: '50', memo: 'Das ist für dich, Bob' },
variables: { identifier: 'bob@baumeister.de', amount: '50', memo: 'Das ist für dich, Bob' },
})
await mutate({
mutation: login,
@ -168,7 +168,7 @@ describe('semaphore', () => {
})
const bobsTransaction = mutate({
mutation: sendCoins,
variables: { email: 'bibi@bloxberg.de', amount: '50', memo: 'Das ist für dich, Bibi' },
variables: { identifier: 'bibi@bloxberg.de', amount: '50', memo: 'Das ist für dich, Bibi' },
})
await mutate({
mutation: login,

View File

@ -0,0 +1,36 @@
import { User as DbUser } from '@entity/User'
import { UserContact as DbUserContact } from '@entity/UserContact'
import LogError from '@/server/LogError'
export const findUserByIdentifier = async (identifier: string): Promise<DbUser | null> => {
let user: DbUser | undefined
if (
/^[0-9a-f]{8,8}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{12,12}$/.exec(identifier)
) {
user = await DbUser.findOne({ where: { gradidoID: identifier } })
if (!user) {
throw new LogError('No user found to given identifier', identifier)
}
} else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) {
const userContact = await DbUserContact.findOne(
{
email: identifier,
emailChecked: true,
},
{ relations: ['user'] },
)
if (!userContact) {
throw new LogError('No user with this credentials', identifier)
}
if (!userContact.user) {
throw new LogError('No user to given contact', identifier)
}
user = userContact.user
user.emailContact = userContact
} else {
// last is alias when implemented
throw new LogError('Unknown identifier type', identifier)
}
return user
}

View File

@ -75,8 +75,8 @@ export const sendActivationEmail = gql`
`
export const sendCoins = gql`
mutation ($email: String!, $amount: Decimal!, $memo: String!) {
sendCoins(email: $email, amount: $amount, memo: $memo)
mutation ($identifier: String!, $amount: Decimal!, $memo: String!) {
sendCoins(identifier: $identifier, amount: $amount, memo: $memo)
}
`