get backend running with new typeorm version

This commit is contained in:
Moriz Wahl 2023-06-26 13:59:42 +02:00
parent 89a6305c45
commit e35104faa8
16 changed files with 167 additions and 73 deletions

View File

@ -40,7 +40,7 @@ module.exports = {
],
// import
'import/export': 'error',
'import/no-deprecated': 'error',
// 'import/no-deprecated': 'error',
'import/no-empty-named-blocks': 'error',
'import/no-extraneous-dependencies': 'error',
'import/no-mutable-exports': 'error',

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { getCustomRepository } from '@dbTools/typeorm'
import { getCustomRepository, IsNull } from '@dbTools/typeorm'
import { Transaction as dbTransaction } from '@entity/Transaction'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { Decimal } from 'decimal.js-light'
@ -57,7 +57,7 @@ export class BalanceResolver {
const linkCount = await dbTransactionLink.count({
where: {
userId: user.id,
redeemedAt: null,
redeemedAt: IsNull(),
// validUntil: MoreThan(new Date()),
},
})

View File

@ -103,7 +103,7 @@ export class ContributionLinkResolver {
@Arg('id', () => Int) id: number,
@Ctx() context: Context,
): Promise<boolean> {
const dbContributionLink = await DbContributionLink.findOne(id)
const dbContributionLink = await DbContributionLink.findOne({ where: { id } })
if (!dbContributionLink) {
throw new LogError('Contribution Link not found', id)
}
@ -130,7 +130,7 @@ export class ContributionLinkResolver {
@Arg('id', () => Int) id: number,
@Ctx() context: Context,
): Promise<ContributionLink> {
const dbContributionLink = await DbContributionLink.findOne(id)
const dbContributionLink = await DbContributionLink.findOne({ where: { id } })
if (!dbContributionLink) {
throw new LogError('Contribution Link not found', id)
}

View File

@ -36,7 +36,7 @@ export class ContributionMessageResolver {
await queryRunner.startTransaction('REPEATABLE READ')
const contributionMessage = DbContributionMessage.create()
try {
const contribution = await DbContribution.findOne({ id: contributionId })
const contribution = await DbContribution.findOne({ where: { id: contributionId } })
if (!contribution) {
throw new LogError('Contribution not found', contributionId)
}
@ -124,7 +124,7 @@ export class ContributionMessageResolver {
if (contribution.userId === moderator.id) {
throw new LogError('Admin can not answer on his own contribution', contributionId)
}
if (!contribution.user.emailContact) {
if (!contribution.user.emailContact && contribution.user.emailId) {
contribution.user.emailContact = await DbUserContact.findOneOrFail({
where: { id: contribution.user.emailId },
})

View File

@ -2890,6 +2890,64 @@ describe('ContributionResolver', () => {
]),
})
})
describe('with query', () => {
it('returns the creations of queried user', async () => {
const result = await query({
query: adminListContributions,
variables: {
currentPage: 1,
pageSize: 2,
order: Order.DESC,
query: 'peter',
},
})
const {
data: { adminListContributions: contributionListObject },
} = await query({
query: adminListContributions,
variables: {
currentPage: 1,
pageSize: 2,
order: Order.DESC,
query: 'peter',
},
})
expect(contributionListObject.contributionList).toHaveLength(3)
expect(contributionListObject).toMatchObject({
contributionCount: 3,
contributionList: expect.arrayContaining([
expect.objectContaining({
amount: expect.decimalEqual(400),
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Herzlich Willkommen bei Gradido!',
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: expect.decimalEqual(100),
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Test env contribution',
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: expect.decimalEqual(200),
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Das war leider zu Viel!',
messagesCount: 0,
state: 'DELETED',
}),
]),
})
})
})
})
})
})

View File

@ -101,7 +101,7 @@ export class ContributionResolver {
@Ctx() context: Context,
): Promise<boolean> {
const user = getUser(context)
const contribution = await DbContribution.findOne(id)
const contribution = await DbContribution.findOne({ where: { id } })
if (!contribution) {
throw new LogError('Contribution not found', id)
}
@ -372,6 +372,8 @@ export class ContributionResolver {
statusFilter?: ContributionStatus[] | null,
@Arg('userId', () => Int, { nullable: true })
userId?: number | null,
@Arg('query', () => String, { nullable: true })
query?: string | null,
): Promise<ContributionListResult> {
const [dbContributions, count] = await findContributions({
order,
@ -381,6 +383,7 @@ export class ContributionResolver {
userId,
relations: ['user', 'messages'],
statusFilter,
query,
})
return new ContributionListResult(
@ -395,7 +398,7 @@ export class ContributionResolver {
@Arg('id', () => Int) id: number,
@Ctx() context: Context,
): Promise<boolean> {
const contribution = await DbContribution.findOne(id)
const contribution = await DbContribution.findOne({ where: { id } })
if (!contribution) {
throw new LogError('Contribution not found', id)
}
@ -409,10 +412,10 @@ export class ContributionResolver {
) {
throw new LogError('Own contribution can not be deleted as admin')
}
const user = await DbUser.findOneOrFail(
{ id: contribution.userId },
{ relations: ['emailContact'] },
)
const user = await DbUser.findOneOrFail({
where: { id: contribution.userId },
relations: ['emailContact'],
})
contribution.contributionStatus = ContributionStatus.DELETED
contribution.deletedBy = moderator.id
await contribution.save()
@ -447,7 +450,7 @@ export class ContributionResolver {
const releaseLock = await TRANSACTIONS_LOCK.acquire()
try {
const clientTimezoneOffset = getClientTimezoneOffset(context)
const contribution = await DbContribution.findOne(id)
const contribution = await DbContribution.findOne({ where: { id } })
if (!contribution) {
throw new LogError('Contribution not found', id)
}
@ -461,10 +464,11 @@ export class ContributionResolver {
if (moderatorUser.id === contribution.userId) {
throw new LogError('Moderator can not confirm own contribution')
}
const user = await DbUser.findOneOrFail(
{ id: contribution.userId },
{ withDeleted: true, relations: ['emailContact'] },
)
const user = await DbUser.findOneOrFail({
where: { id: contribution.userId },
withDeleted: true,
relations: ['emailContact'],
})
if (user.deletedAt) {
throw new LogError('Can not confirm contribution since the user was deleted')
}
@ -565,9 +569,11 @@ export class ContributionResolver {
@Ctx() context: Context,
): Promise<boolean> {
const contributionToUpdate = await DbContribution.findOne({
id,
confirmedAt: IsNull(),
deniedBy: IsNull(),
where: {
id,
confirmedAt: IsNull(),
deniedBy: IsNull(),
},
})
if (!contributionToUpdate) {
throw new LogError('Contribution not found', id)
@ -582,10 +588,10 @@ export class ContributionResolver {
)
}
const moderator = getUser(context)
const user = await DbUser.findOne(
{ id: contributionToUpdate.userId },
{ relations: ['emailContact'] },
)
const user = await DbUser.findOne({
where: { id: contributionToUpdate.userId },
relations: ['emailContact'],
})
if (!user) {
throw new LogError('Could not find User of the Contribution', contributionToUpdate.userId)
}

View File

@ -108,7 +108,7 @@ export class TransactionLinkResolver {
): Promise<boolean> {
const user = getUser(context)
const transactionLink = await DbTransactionLink.findOne({ id })
const transactionLink = await DbTransactionLink.findOne({ where: { id } })
if (!transactionLink) {
throw new LogError('Transaction link not found', id)
}
@ -138,17 +138,22 @@ export class TransactionLinkResolver {
@Query(() => QueryLinkResult)
async queryTransactionLink(@Arg('code') code: string): Promise<typeof QueryLinkResult> {
if (code.match(/^CL-/)) {
const contributionLink = await DbContributionLink.findOneOrFail(
{ code: code.replace('CL-', '') },
{ withDeleted: true },
)
const contributionLink = await DbContributionLink.findOneOrFail({
where: { code: code.replace('CL-', '') },
withDeleted: true,
})
return new ContributionLink(contributionLink)
} else {
const transactionLink = await DbTransactionLink.findOneOrFail({ code }, { withDeleted: true })
const user = await DbUser.findOneOrFail({ id: transactionLink.userId })
const transactionLink = await DbTransactionLink.findOneOrFail({
where: { code },
withDeleted: true,
})
const user = await DbUser.findOneOrFail({ where: { id: transactionLink.userId } })
let redeemedBy: User | null = null
if (transactionLink?.redeemedBy) {
redeemedBy = new User(await DbUser.findOneOrFail({ id: transactionLink.redeemedBy }))
redeemedBy = new User(
await DbUser.findOneOrFail({ where: { id: transactionLink.redeemedBy } }),
)
}
return new TransactionLink(transactionLink, new User(user), redeemedBy)
}
@ -191,7 +196,7 @@ export class TransactionLinkResolver {
throw new LogError('Contribution link is no longer valid', contributionLink.validTo)
}
}
let alreadyRedeemed: DbContribution | undefined
let alreadyRedeemed: DbContribution | null
switch (contributionLink.cycle) {
case ContributionCycleType.ONCE: {
alreadyRedeemed = await queryRunner.manager
@ -302,15 +307,17 @@ export class TransactionLinkResolver {
return true
} else {
const now = new Date()
const transactionLink = await DbTransactionLink.findOne({ code })
const transactionLink = await DbTransactionLink.findOne({ where: { code } })
if (!transactionLink) {
throw new LogError('Transaction link not found', code)
}
const linkedUser = await DbUser.findOne(
{ id: transactionLink.userId },
{ relations: ['emailContact'] },
)
const linkedUser = await DbUser.findOne({
where: {
id: transactionLink.userId,
},
relations: ['emailContact'],
})
if (!linkedUser) {
throw new LogError('Linked user not found for given link', transactionLink.userId)
@ -378,7 +385,7 @@ export class TransactionLinkResolver {
@Arg('userId', () => Int)
userId: number,
): Promise<TransactionLinkResult> {
const user = await DbUser.findOne({ id: userId })
const user = await DbUser.findOne({ where: { id: userId } })
if (!user) {
throw new LogError('Could not find requested User', userId)
}

View File

@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import { getConnection, getCustomRepository, IsNull, Not } from '@dbTools/typeorm'
import { getConnection, getCustomRepository, IsNull, Not, Equal } from '@dbTools/typeorm'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User'
@ -278,7 +278,7 @@ export class UserResolver {
eventRegisterRedeem.involvedContributionLink = contributionLink
}
} else {
const transactionLink = await DbTransactionLink.findOne({ where: { code: redeemCode } })
const transactionLink = await DbTransactionLink.findOne({ where: { code: redeemCode } })
logger.info('redeemCode found transactionLink', transactionLink)
if (transactionLink) {
dbUser.referrerId = transactionLink.userId
@ -403,8 +403,8 @@ export class UserResolver {
}
// load code
const userContact = await DbUserContact.findOne({
where: { emailVerificationCode: code },
const userContact = await DbUserContact.findOneOrFail({
where: { emailVerificationCode: Equal(BigInt(code)) },
relations: ['user'],
}).catch(() => {
throw new LogError('Could not login with emailVerificationCode')
@ -474,7 +474,9 @@ export class UserResolver {
@Query(() => Boolean)
async queryOptIn(@Arg('optIn') optIn: string): Promise<boolean> {
logger.info(`queryOptIn(${optIn})...`)
const userContact = await DbUserContact.findOneOrFail({ where: { emailVerificationCode: optIn } })
const userContact = await DbUserContact.findOneOrFail({
where: { emailVerificationCode: Equal(BigInt(optIn)) },
})
logger.debug('found optInCode', userContact)
// Code is only valid for `CONFIG.EMAIL_CODE_VALID_TIME` minutes
if (!isEmailVerificationCodeValid(userContact.updatedAt || userContact.createdAt)) {
@ -734,7 +736,7 @@ export class UserResolver {
}
await user.save()
await EVENT_ADMIN_USER_ROLE_SET(user, moderator)
const newUser = await DbUser.findOne({ where: { id: userId } })
const newUser = await DbUser.findOne({ where: { id: userId } })
return newUser ? newUser.isAdmin : null
}
@ -744,7 +746,7 @@ export class UserResolver {
@Arg('userId', () => Int) userId: number,
@Ctx() context: Context,
): Promise<Date | null> {
const user = await DbUser.findOne({ id: userId })
const user = await DbUser.findOne({ where: { id: userId } })
// user exists ?
if (!user) {
throw new LogError('Could not find user with given ID', userId)
@ -757,7 +759,7 @@ export class UserResolver {
// soft-delete user
await user.softRemove()
await EVENT_ADMIN_USER_DELETE(user, moderator)
const newUser = await DbUser.findOne({ where: { id: userId }, withDeleted: true })
const newUser = await DbUser.findOne({ where: { id: userId }, withDeleted: true })
return newUser ? newUser.deletedAt : null
}
@ -822,7 +824,7 @@ export async function findUserByEmail(email: string): Promise<DbUser> {
const dbUserContact = await DbUserContact.findOneOrFail({
where: { email },
withDeleted: true,
relations: ['user']
relations: ['user'],
}).catch(() => {
throw new LogError('No user with this credentials', email)
})

View File

@ -1,4 +1,4 @@
import { In } from '@dbTools/typeorm'
import { In, Like, FindOperator } from '@dbTools/typeorm'
import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionStatus } from '@enum/ContributionStatus'
@ -12,27 +12,48 @@ interface FindContributionsOptions {
relations?: string[]
userId?: number | null
statusFilter?: ContributionStatus[] | null
query?: string | null
}
export const findContributions = async (
options: FindContributionsOptions,
): Promise<[DbContribution[], number]> => {
const { order, currentPage, pageSize, withDeleted, relations, userId, statusFilter } = {
const { order, currentPage, pageSize, withDeleted, relations, userId, statusFilter, query } = {
withDeleted: false,
relations: [],
query: '',
...options,
}
const where: {
userId?: number | undefined
contributionStatus?: FindOperator<ContributionStatus> | undefined
user?: Record<string, FindOperator<string>>[] | undefined
} = {
...(statusFilter?.length && { contributionStatus: In(statusFilter) }),
...(userId && { userId }),
}
if (query) {
where.user = [
{ firstName: Like(`%${query}%`) },
{ lastName: Like(`%${query}%`) },
// emailContact: { email: Like(`%${query}%`) },
]
}
return DbContribution.findAndCount({
where: {
...(statusFilter?.length && { contributionStatus: In(statusFilter) }),
...(userId && { userId }),
relations: {
user: {
emailContact: true,
},
messages: true,
},
withDeleted,
where,
order: {
createdAt: order,
id: order,
},
relations,
skip: (currentPage - 1) * pageSize,
take: pageSize,
})

View File

@ -7,20 +7,20 @@ import { LogError } from '@/server/LogError'
import { VALID_ALIAS_REGEX } from './validateAlias'
export const findUserByIdentifier = async (identifier: string): Promise<DbUser> => {
let user: DbUser | undefined
let user: DbUser | null
if (validate(identifier) && version(identifier) === 4) {
user = await DbUser.findOne({ where: { gradidoID: identifier }, relations: ['emailContact'] })
if (!user) {
throw new LogError('No user found to given identifier', identifier)
}
} else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) {
const userContact = await DbUserContact.findOne(
{
const userContact = await DbUserContact.findOne({
where: {
email: identifier,
emailChecked: true,
},
{ relations: ['user'] },
)
relations: ['user'],
})
if (!userContact) {
throw new LogError('No user with this credentials', identifier)
}

View File

@ -3,12 +3,10 @@ import { Transaction as DbTransaction } from '@entity/Transaction'
export const getLastTransaction = async (
userId: number,
relations?: string[],
): Promise<DbTransaction | undefined> => {
return DbTransaction.findOne(
{ userId },
{
order: { balanceDate: 'DESC', id: 'DESC' },
relations,
},
)
): Promise<DbTransaction | null> => {
return DbTransaction.findOne({
where: { userId },
order: { balanceDate: 'DESC', id: 'DESC' },
relations,
})
}

View File

@ -1,4 +1,4 @@
import { MoreThan } from '@dbTools/typeorm'
import { MoreThan, IsNull } from '@dbTools/typeorm'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User'
@ -22,7 +22,7 @@ export async function transactionLinkList(
const [transactionLinks, count] = await DbTransactionLink.findAndCount({
where: {
userId: user.id,
...(!withRedeemed && { redeemedBy: null }),
...(!withRedeemed && { redeemedBy: IsNull() }),
...(!withExpired && { validUntil: MoreThan(new Date()) }),
},
withDeleted,

View File

@ -235,6 +235,7 @@ export const adminListContributions = gql`
$order: Order = DESC
$statusFilter: [ContributionStatus!]
$userId: Int
$query: String
) {
adminListContributions(
currentPage: $currentPage
@ -242,6 +243,7 @@ export const adminListContributions = gql`
order: $order
statusFilter: $statusFilter
userId: $userId
query: $query
) {
contributionCount
contributionList {

View File

@ -15,7 +15,7 @@ export interface Context {
clientTimezoneOffset?: number
gradidoID?: string
// hack to use less DB calls for Balance Resolver
lastTransaction?: dbTransaction
lastTransaction?: dbTransaction | null
transactionCount?: number
linkCount?: number
sumHoldAvailableAmount?: Decimal

View File

@ -4,7 +4,7 @@ import { backendLogger as logger } from '@/server/logger'
const getDBVersion = async (): Promise<string | null> => {
try {
const dbVersion = await Migration.findOne({ order: { version: 'DESC' } })
const [dbVersion] = await Migration.find({ order: { version: 'DESC' }, take: 1 })
return dbVersion ? dbVersion.fileName : null
} catch (error) {
logger.error(error)

View File

@ -1,6 +1,6 @@
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
export async function hasElopageBuys(email: string): Promise<boolean> {
const elopageBuyCount = await LoginElopageBuys.count({ payerEmail: email })
const elopageBuyCount = await LoginElopageBuys.count({ where: { payerEmail: email } })
return elopageBuyCount > 0
}