From d391aa349155bd9b1db7639bfd1390e02fdef40b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 1 Oct 2021 21:59:34 +0200 Subject: [PATCH] resolvers --- backend/src/graphql/resolvers/GdtResolver.ts | 5 +- .../graphql/resolvers/KlicktippResolver.ts | 4 +- .../graphql/resolvers/TransactionResolver.ts | 209 +++++++++++++++++- backend/src/graphql/resolvers/UserResolver.ts | 12 +- .../src/graphql/resolvers/listTransactions.ts | 187 ---------------- 5 files changed, 212 insertions(+), 205 deletions(-) delete mode 100644 backend/src/graphql/resolvers/listTransactions.ts diff --git a/backend/src/graphql/resolvers/GdtResolver.ts b/backend/src/graphql/resolvers/GdtResolver.ts index 8ea006313..0552c1510 100644 --- a/backend/src/graphql/resolvers/GdtResolver.ts +++ b/backend/src/graphql/resolvers/GdtResolver.ts @@ -5,9 +5,10 @@ import { Resolver, Query, Args, Ctx, Authorized } from 'type-graphql' import { getCustomRepository } from 'typeorm' import CONFIG from '../../config' import { GdtEntryList } from '../models/GdtEntryList' -import { GdtTransactionSessionIdInput } from '../inputs/GdtInputs' +import Paginated from '../args/Paginated' import { apiGet } from '../../apis/HttpRequest' import { UserRepository } from '../../typeorm/repository/User' +import { Order } from '../enum/Order' @Resolver() export class GdtResolver { @@ -16,7 +17,7 @@ export class GdtResolver { // eslint-disable-next-line @typescript-eslint/no-explicit-any async listGDTEntries( @Args() - { currentPage = 1, pageSize = 5, order = 'DESC' }: GdtTransactionSessionIdInput, + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, @Ctx() context: any, ): Promise { // load user diff --git a/backend/src/graphql/resolvers/KlicktippResolver.ts b/backend/src/graphql/resolvers/KlicktippResolver.ts index 6d8381993..a76e9f6bb 100644 --- a/backend/src/graphql/resolvers/KlicktippResolver.ts +++ b/backend/src/graphql/resolvers/KlicktippResolver.ts @@ -8,7 +8,7 @@ import { unsubscribe, signIn, } from '../../apis/KlicktippController' -import { SubscribeNewsletterArguments } from '../inputs/KlickTippInputs' +import SubscribeNewsletterArgs from '../args/SubscribeNewsletterArgs' @Resolver() export class KlicktippResolver { @@ -33,7 +33,7 @@ export class KlicktippResolver { @Authorized() @Mutation(() => Boolean) async subscribeNewsletter( - @Args() { email, language }: SubscribeNewsletterArguments, + @Args() { email, language }: SubscribeNewsletterArgs, ): Promise { return await signIn(email, language) } diff --git a/backend/src/graphql/resolvers/TransactionResolver.ts b/backend/src/graphql/resolvers/TransactionResolver.ts index cd07a94f5..f079da9ca 100644 --- a/backend/src/graphql/resolvers/TransactionResolver.ts +++ b/backend/src/graphql/resolvers/TransactionResolver.ts @@ -3,22 +3,217 @@ import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { getCustomRepository } from 'typeorm' + import CONFIG from '../../config' -import { TransactionList } from '../models/Transaction' -import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput' -import { apiGet, apiPost } from '../../apis/HttpRequest' + +import { Transaction } from '../models/Transaction' +import { TransactionList } from '../models/TransactionList' + +import TransactionSendArgs from '../args/TransactionSendArgs' +import Paginated from '../args/Paginated' + +import { Order } from '../enum/Order' + import { BalanceRepository } from '../../typeorm/repository/Balance' import { UserRepository } from '../../typeorm/repository/User' -import listTransactions from './listTransactions' +import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' +import { TransactionRepository } from '../../typeorm/repository/Transaction' + +import { User as dbUser } from '../../typeorm/entity/User' +import { UserTransaction as dbUserTransaction } from '../../typeorm/entity/UserTransaction' +import { Transaction as dbTransaction } from '../../typeorm/entity/Transaction' + +import { apiGet, apiPost } from '../../apis/HttpRequest' import { roundFloorFrom4 } from '../../util/round' -import { calculateDecay } from '../../util/decay' +import { calculateDecay, calculateDecayWithInterval } from '../../util/decay' +import { TransactionTypeId } from '../enum/TransactionTypeId' +import { TransactionType } from '../enum/TransactionType' + +// Helper function +async function calculateAndAddDecayTransactions( + userTransactions: dbUserTransaction[], + user: dbUser, + decay: boolean, + skipFirstTransaction: boolean, +): Promise { + const finalTransactions: Transaction[] = [] + const transactionIds: number[] = [] + const involvedUserIds: number[] = [] + + userTransactions.forEach((userTransaction: dbUserTransaction) => { + transactionIds.push(userTransaction.transactionId) + }) + + const transactionRepository = getCustomRepository(TransactionRepository) + const transactions = await transactionRepository.joinFullTransactionsByIds(transactionIds) + + const transactionIndiced: dbTransaction[] = [] + transactions.forEach((transaction: dbTransaction) => { + transactionIndiced[transaction.id] = transaction + if (transaction.transactionTypeId === 2) { + involvedUserIds.push(transaction.transactionSendCoin.userId) + involvedUserIds.push(transaction.transactionSendCoin.recipiantUserId) + } + }) + // remove duplicates + // https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates + const involvedUsersUnique = involvedUserIds.filter((v, i, a) => a.indexOf(v) === i) + const userRepository = getCustomRepository(UserRepository) + const userIndiced = await userRepository.getUsersIndiced(involvedUsersUnique) + + const decayStartTransaction = await transactionRepository.findDecayStartBlock() + + for (let i = 0; i < userTransactions.length; i++) { + const userTransaction = userTransactions[i] + const transaction = transactionIndiced[userTransaction.transactionId] + const finalTransaction = new Transaction() + finalTransaction.transactionId = transaction.id + finalTransaction.date = transaction.received.toISOString() + finalTransaction.memo = transaction.memo + finalTransaction.totalBalance = roundFloorFrom4(userTransaction.balance) + const prev = i > 0 ? userTransactions[i - 1] : null + + if (prev && prev.balance > 0) { + const current = userTransaction + const decay = await calculateDecayWithInterval( + prev.balance, + prev.balanceDate, + current.balanceDate, + ) + const balance = prev.balance - decay.balance + + if (balance) { + finalTransaction.decay = decay + finalTransaction.decay.balance = roundFloorFrom4(balance) + if ( + decayStartTransaction && + prev.transactionId < decayStartTransaction.id && + current.transactionId > decayStartTransaction.id + ) { + finalTransaction.decay.decayStartBlock = ( + decayStartTransaction.received.getTime() / 1000 + ).toString() + } + } + } + + // sender or receiver when user has sended money + // group name if creation + // type: gesendet / empfangen / geschöpft + // transaktion nr / id + // date + // balance + if (userTransaction.transactionTypeId === TransactionTypeId.CREATION) { + // creation + const creation = transaction.transactionCreation + + finalTransaction.name = 'Gradido Akademie' + finalTransaction.type = TransactionType.CREATION + // finalTransaction.targetDate = creation.targetDate + finalTransaction.balance = roundFloorFrom4(creation.amount) + } else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) { + // send coin + const sendCoin = transaction.transactionSendCoin + let otherUser: dbUser | undefined + finalTransaction.balance = roundFloorFrom4(sendCoin.amount) + if (sendCoin.userId === user.id) { + finalTransaction.type = TransactionType.SEND + otherUser = userIndiced[sendCoin.recipiantUserId] + // finalTransaction.pubkey = sendCoin.recipiantPublic + } else if (sendCoin.recipiantUserId === user.id) { + finalTransaction.type = TransactionType.RECIEVE + otherUser = userIndiced[sendCoin.userId] + // finalTransaction.pubkey = sendCoin.senderPublic + } else { + throw new Error('invalid transaction') + } + if (otherUser) { + finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName + finalTransaction.email = otherUser.email + } + } + if (i > 0 || !skipFirstTransaction) { + finalTransactions.push(finalTransaction) + } + + if (i === userTransactions.length - 1 && decay) { + const now = new Date() + const decay = await calculateDecayWithInterval( + userTransaction.balance, + userTransaction.balanceDate, + now.getTime(), + ) + const balance = userTransaction.balance - decay.balance + if (balance) { + const decayTransaction = new Transaction() + decayTransaction.type = 'decay' + decayTransaction.balance = roundFloorFrom4(balance) + decayTransaction.decayDuration = decay.decayDuration + decayTransaction.decayStart = decay.decayStart + decayTransaction.decayEnd = decay.decayEnd + finalTransactions.push(decayTransaction) + } + } + } + + return finalTransactions +} + +// Helper function +async function listTransactions( + currentPage: number, + pageSize: number, + order: Order, + user: dbUser, +): Promise { + let limit = pageSize + let offset = 0 + let skipFirstTransaction = false + if (currentPage > 1) { + offset = (currentPage - 1) * pageSize - 1 + limit++ + } + + if (offset && order === Order.ASC) { + offset-- + } + const userTransactionRepository = getCustomRepository(UserTransactionRepository) + let [userTransactions, userTransactionsCount] = await userTransactionRepository.findByUserPaged( + user.id, + limit, + offset, + order, + ) + skipFirstTransaction = userTransactionsCount > offset + limit + const decay = !(currentPage > 1) + let transactions: Transaction[] = [] + if (userTransactions.length) { + if (order === Order.DESC) { + userTransactions = userTransactions.reverse() + } + transactions = await calculateAndAddDecayTransactions( + userTransactions, + user, + decay, + skipFirstTransaction, + ) + if (order === Order.DESC) { + transactions = transactions.reverse() + } + } + + const transactionList = new TransactionList() + transactionList.count = userTransactionsCount + transactionList.transactions = transactions + return transactionList +} @Resolver() export class TransactionResolver { @Authorized() @Query(() => TransactionList) async transactionList( - @Args() { firstPage = 1, items = 25, order = 'DESC' }: TransactionListInput, + @Args() { currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated, @Ctx() context: any, ): Promise { // get public key for current logged in user @@ -29,7 +224,7 @@ export class TransactionResolver { const userRepository = getCustomRepository(UserRepository) const userEntity = await userRepository.findByPubkeyHex(result.data.user.public_hex) - const transactions = await listTransactions(firstPage, items, order, userEntity) + const transactions = await listTransactions(currentPage, pageSize, order, userEntity) // get gdt sum const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, { diff --git a/backend/src/graphql/resolvers/UserResolver.ts b/backend/src/graphql/resolvers/UserResolver.ts index 8997fb264..2c6e94f1d 100644 --- a/backend/src/graphql/resolvers/UserResolver.ts +++ b/backend/src/graphql/resolvers/UserResolver.ts @@ -9,13 +9,11 @@ import { SendPasswordResetEmailResponse } from '../models/SendPasswordResetEmail import { UpdateUserInfosResponse } from '../models/UpdateUserInfosResponse' import { User } from '../models/User' import encode from '../../jwt/encode' -import { - ChangePasswordArgs, - CheckUsernameArgs, - CreateUserArgs, - UnsecureLoginArgs, - UpdateUserInfosArgs, -} from '../inputs/LoginUserInput' +import ChangePasswordArgs from '../args/ChangePasswordArgs' +import CheckUsernameArgs from '../args/CheckUsernameArgs' +import CreateUserArgs from '../args/CreateUserArgs' +import UnsecureLoginArgs from '../args/UnsecureLoginArgs' +import UpdateUserInfosArgs from '../args/UpdateUserInfosArgs' import { apiPost, apiGet } from '../../apis/HttpRequest' import { klicktippRegistrationMiddleware, diff --git a/backend/src/graphql/resolvers/listTransactions.ts b/backend/src/graphql/resolvers/listTransactions.ts deleted file mode 100644 index bf84e17d4..000000000 --- a/backend/src/graphql/resolvers/listTransactions.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { getCustomRepository } from 'typeorm' -import { User as dbUser } from '../../typeorm/entity/User' -import { UserRepository } from '../../typeorm/repository/User' -import { TransactionList, Transaction } from '../models/Transaction' -import { UserTransaction as dbUserTransaction } from '../../typeorm/entity/UserTransaction' -import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' -import { Transaction as dbTransaction } from '../../typeorm/entity/Transaction' -import { TransactionRepository } from '../../typeorm/repository/Transaction' -import { calculateDecayWithInterval } from '../../util/decay' -import { roundFloorFrom4 } from '../../util/round' - -async function calculateAndAddDecayTransactions( - userTransactions: dbUserTransaction[], - user: dbUser, - decay: boolean, - skipFirstTransaction: boolean, -): Promise { - const finalTransactions: Transaction[] = [] - const transactionIds: number[] = [] - const involvedUserIds: number[] = [] - - userTransactions.forEach((userTransaction: dbUserTransaction) => { - transactionIds.push(userTransaction.transactionId) - }) - - const transactionRepository = getCustomRepository(TransactionRepository) - const transactions = await transactionRepository.joinFullTransactionsByIds(transactionIds) - - const transactionIndiced: dbTransaction[] = [] - transactions.forEach((transaction: dbTransaction) => { - transactionIndiced[transaction.id] = transaction - if (transaction.transactionTypeId === 2) { - involvedUserIds.push(transaction.transactionSendCoin.userId) - involvedUserIds.push(transaction.transactionSendCoin.recipiantUserId) - } - }) - // remove duplicates - // https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates - const involvedUsersUnique = involvedUserIds.filter((v, i, a) => a.indexOf(v) === i) - const userRepository = getCustomRepository(UserRepository) - const userIndiced = await userRepository.getUsersIndiced(involvedUsersUnique) - - const decayStartTransaction = await transactionRepository.findDecayStartBlock() - - for (let i = 0; i < userTransactions.length; i++) { - const userTransaction = userTransactions[i] - const transaction = transactionIndiced[userTransaction.transactionId] - const finalTransaction = new Transaction() - finalTransaction.transactionId = transaction.id - finalTransaction.date = transaction.received.toISOString() - finalTransaction.memo = transaction.memo - finalTransaction.totalBalance = roundFloorFrom4(userTransaction.balance) - const prev = i > 0 ? userTransactions[i - 1] : null - - if (prev && prev.balance > 0) { - const current = userTransaction - const decay = await calculateDecayWithInterval( - prev.balance, - prev.balanceDate, - current.balanceDate, - ) - const balance = prev.balance - decay.balance - - if (balance) { - finalTransaction.decay = decay - finalTransaction.decay.balance = roundFloorFrom4(balance) - if ( - decayStartTransaction && - prev.transactionId < decayStartTransaction.id && - current.transactionId > decayStartTransaction.id - ) { - finalTransaction.decay.decayStartBlock = ( - decayStartTransaction.received.getTime() / 1000 - ).toString() - } - } - } - - // sender or receiver when user has sended money - // group name if creation - // type: gesendet / empfangen / geschöpft - // transaktion nr / id - // date - // balance - if (userTransaction.transactionTypeId === 1) { - // creation - const creation = transaction.transactionCreation - - finalTransaction.name = 'Gradido Akademie' - finalTransaction.type = 'creation' - // finalTransaction.targetDate = creation.targetDate - finalTransaction.balance = roundFloorFrom4(creation.amount) - } else if (userTransaction.transactionTypeId === 2) { - // send coin - const sendCoin = transaction.transactionSendCoin - let otherUser: dbUser | undefined - finalTransaction.balance = roundFloorFrom4(sendCoin.amount) - if (sendCoin.userId === user.id) { - finalTransaction.type = 'send' - otherUser = userIndiced[sendCoin.recipiantUserId] - // finalTransaction.pubkey = sendCoin.recipiantPublic - } else if (sendCoin.recipiantUserId === user.id) { - finalTransaction.type = 'receive' - otherUser = userIndiced[sendCoin.userId] - // finalTransaction.pubkey = sendCoin.senderPublic - } else { - throw new Error('invalid transaction') - } - if (otherUser) { - finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName - finalTransaction.email = otherUser.email - } - } - if (i > 0 || !skipFirstTransaction) { - finalTransactions.push(finalTransaction) - } - - if (i === userTransactions.length - 1 && decay) { - const now = new Date() - const decay = await calculateDecayWithInterval( - userTransaction.balance, - userTransaction.balanceDate, - now.getTime(), - ) - const balance = userTransaction.balance - decay.balance - if (balance) { - const decayTransaction = new Transaction() - decayTransaction.type = 'decay' - decayTransaction.balance = roundFloorFrom4(balance) - decayTransaction.decayDuration = decay.decayDuration - decayTransaction.decayStart = decay.decayStart - decayTransaction.decayEnd = decay.decayEnd - finalTransactions.push(decayTransaction) - } - } - } - - return finalTransactions -} - -export default async function listTransactions( - firstPage: number, - items: number, - order: 'ASC' | 'DESC', - user: dbUser, -): Promise { - let limit = items - let offset = 0 - let skipFirstTransaction = false - if (firstPage > 1) { - offset = (firstPage - 1) * items - 1 - limit++ - } - - if (offset && order === 'ASC') { - offset-- - } - const userTransactionRepository = getCustomRepository(UserTransactionRepository) - let [userTransactions, userTransactionsCount] = await userTransactionRepository.findByUserPaged( - user.id, - limit, - offset, - order, - ) - skipFirstTransaction = userTransactionsCount > offset + limit - const decay = !(firstPage > 1) - let transactions: Transaction[] = [] - if (userTransactions.length) { - if (order === 'DESC') { - userTransactions = userTransactions.reverse() - } - transactions = await calculateAndAddDecayTransactions( - userTransactions, - user, - decay, - skipFirstTransaction, - ) - if (order === 'DESC') { - transactions = transactions.reverse() - } - } - - const transactionList = new TransactionList() - transactionList.count = userTransactionsCount - transactionList.transactions = transactions - return transactionList -}