mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
commit
3042f49d0c
13
backend/src/graphql/args/ChangePasswordArgs.ts
Normal file
13
backend/src/graphql/args/ChangePasswordArgs.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class ChangePasswordArgs {
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
}
|
||||
10
backend/src/graphql/args/CheckUsernameArgs.ts
Normal file
10
backend/src/graphql/args/CheckUsernameArgs.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class CheckUsernameArgs {
|
||||
@Field(() => String)
|
||||
username: string
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
groupId?: number
|
||||
}
|
||||
19
backend/src/graphql/args/CreateUserArgs.ts
Normal file
19
backend/src/graphql/args/CreateUserArgs.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class CreateUserArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string
|
||||
|
||||
@Field(() => String)
|
||||
lastName: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
|
||||
@Field(() => String)
|
||||
language: string
|
||||
}
|
||||
14
backend/src/graphql/args/Paginated.ts
Normal file
14
backend/src/graphql/args/Paginated.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
import { Order } from '../enum/Order'
|
||||
|
||||
@ArgsType()
|
||||
export default class Paginated {
|
||||
@Field(() => Int, { nullable: true })
|
||||
currentPage?: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
pageSize?: number
|
||||
|
||||
@Field(() => Order, { nullable: true })
|
||||
order?: Order
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class SubscribeNewsletterArguments {
|
||||
export default class SubscribeNewsletterArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
13
backend/src/graphql/args/TransactionSendArgs.ts
Normal file
13
backend/src/graphql/args/TransactionSendArgs.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class TransactionSendArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
amount: number
|
||||
|
||||
@Field(() => String)
|
||||
memo: string
|
||||
}
|
||||
10
backend/src/graphql/args/UnsecureLoginArgs.ts
Normal file
10
backend/src/graphql/args/UnsecureLoginArgs.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class UnsecureLoginArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
}
|
||||
31
backend/src/graphql/args/UpdateUserInfosArgs.ts
Normal file
31
backend/src/graphql/args/UpdateUserInfosArgs.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export default class UpdateUserInfosArgs {
|
||||
@Field(() => String)
|
||||
email!: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
firstName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
lastName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
description?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
username?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
language?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
publisherId?: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
password?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
passwordNew?: string
|
||||
}
|
||||
16
backend/src/graphql/enum/GdtEntryType.ts
Normal file
16
backend/src/graphql/enum/GdtEntryType.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum GdtEntryType {
|
||||
FORM = 1,
|
||||
CVS = 2,
|
||||
ELOPAGE = 3,
|
||||
ELOPAGE_PUBLISHER = 4,
|
||||
DIGISTORE = 5,
|
||||
CVS2 = 6,
|
||||
GLOBAL_MODIFICATOR = 7,
|
||||
}
|
||||
|
||||
registerEnumType(GdtEntryType, {
|
||||
name: 'GdtEntryType', // this one is mandatory
|
||||
description: 'Gdt Entry Source Type', // this one is optional
|
||||
})
|
||||
11
backend/src/graphql/enum/Order.ts
Normal file
11
backend/src/graphql/enum/Order.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum Order {
|
||||
ASC = 'ASC',
|
||||
DESC = 'DESC',
|
||||
}
|
||||
|
||||
registerEnumType(Order, {
|
||||
name: 'Order', // this one is mandatory
|
||||
description: 'Order direction - ascending or descending', // this one is optional
|
||||
})
|
||||
12
backend/src/graphql/enum/TransactionType.ts
Normal file
12
backend/src/graphql/enum/TransactionType.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum TransactionType {
|
||||
CREATION = 'creation',
|
||||
SEND = 'send',
|
||||
RECIEVE = 'receive',
|
||||
}
|
||||
|
||||
registerEnumType(TransactionType, {
|
||||
name: 'TransactionType', // this one is mandatory
|
||||
description: 'Name of the Type of the transaction', // this one is optional
|
||||
})
|
||||
11
backend/src/graphql/enum/TransactionTypeId.ts
Normal file
11
backend/src/graphql/enum/TransactionTypeId.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
export enum TransactionTypeId {
|
||||
CREATION = 1,
|
||||
SEND = 2,
|
||||
}
|
||||
|
||||
registerEnumType(TransactionTypeId, {
|
||||
name: 'TransactionTypeId', // this one is mandatory
|
||||
description: 'Type of the transaction', // this one is optional
|
||||
})
|
||||
@ -1,28 +0,0 @@
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class GdtTransactionInput {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
currentPage?: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
pageSize?: number
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
order?: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class GdtTransactionSessionIdInput {
|
||||
@Field(() => Int, { nullable: true })
|
||||
currentPage?: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
pageSize?: number
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
order?: string
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
import { ArgsType, Field } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class UnsecureLoginArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class CreateUserArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
firstName: string
|
||||
|
||||
@Field(() => String)
|
||||
lastName: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
|
||||
@Field(() => String)
|
||||
language: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class ChangePasswordArgs {
|
||||
@Field(() => Number)
|
||||
sessionId: number
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => String)
|
||||
password: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class UpdateUserInfosArgs {
|
||||
@Field(() => String)
|
||||
email!: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
firstName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
lastName?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
description?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
username?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
language?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
publisherId?: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
password?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
passwordNew?: string
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class CheckUsernameArgs {
|
||||
@Field(() => String)
|
||||
username: string
|
||||
|
||||
@Field(() => Number, { nullable: true })
|
||||
groupId?: number
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
import { ArgsType, Field, Int } from 'type-graphql'
|
||||
|
||||
@ArgsType()
|
||||
export class TransactionListInput {
|
||||
@Field(() => Int)
|
||||
firstPage: number
|
||||
|
||||
@Field(() => Int)
|
||||
items: number
|
||||
|
||||
@Field(() => String)
|
||||
order: 'ASC' | 'DESC'
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
export class TransactionSendArgs {
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
amount: number
|
||||
|
||||
@Field(() => String)
|
||||
memo: string
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
import { Transaction } from '../../typeorm/entity/Transaction'
|
||||
|
||||
@ObjectType()
|
||||
export class Decay {
|
||||
|
||||
@ -1,16 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
export enum GdtEntryType {
|
||||
FORM = 1,
|
||||
CVS = 2,
|
||||
ELOPAGE = 3,
|
||||
ELOPAGE_PUBLISHER = 4,
|
||||
DIGISTORE = 5,
|
||||
CVS2 = 6,
|
||||
GLOBAL_MODIFICATOR = 7,
|
||||
}
|
||||
import { GdtEntryType } from '../enum/GdtEntryType'
|
||||
|
||||
@ObjectType()
|
||||
export class GdtEntry {
|
||||
@ -46,7 +37,7 @@ export class GdtEntry {
|
||||
@Field(() => String)
|
||||
couponCode: string
|
||||
|
||||
@Field(() => Number)
|
||||
@Field(() => GdtEntryType)
|
||||
gdtEntryType: GdtEntryType
|
||||
|
||||
@Field(() => Number)
|
||||
|
||||
@ -3,20 +3,6 @@
|
||||
import { GdtEntry } from './GdtEntry'
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class GdtSumPerEmail {
|
||||
constructor(email: string, summe: number) {
|
||||
this.email = email
|
||||
this.summe = summe
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
summe: number
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class GdtEntryList {
|
||||
constructor(json: any) {
|
||||
|
||||
19
backend/src/graphql/models/GdtSumPerEmail.ts
Normal file
19
backend/src/graphql/models/GdtSumPerEmail.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/*
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
|
||||
@ObjectType()
|
||||
export class GdtSumPerEmail {
|
||||
constructor(email: string, summe: number) {
|
||||
this.email = email
|
||||
this.summe = summe
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
email: string
|
||||
|
||||
@Field(() => Number)
|
||||
summe: number
|
||||
}
|
||||
*/
|
||||
@ -53,32 +53,3 @@ export class Transaction {
|
||||
@Field({ nullable: true })
|
||||
decay?: Decay
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionList {
|
||||
constructor() {
|
||||
this.gdtSum = 0
|
||||
this.count = 0
|
||||
this.balance = 0
|
||||
this.decay = 0
|
||||
this.decayDate = ''
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
gdtSum: number
|
||||
|
||||
@Field(() => Number)
|
||||
count: number
|
||||
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
@Field(() => Number)
|
||||
decay: number
|
||||
|
||||
@Field(() => String)
|
||||
decayDate: string
|
||||
|
||||
@Field(() => [Transaction])
|
||||
transactions: Transaction[]
|
||||
}
|
||||
|
||||
33
backend/src/graphql/models/TransactionList.ts
Normal file
33
backend/src/graphql/models/TransactionList.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field } from 'type-graphql'
|
||||
import { Transaction } from './Transaction'
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionList {
|
||||
constructor() {
|
||||
this.gdtSum = 0
|
||||
this.count = 0
|
||||
this.balance = 0
|
||||
this.decay = 0
|
||||
this.decayDate = ''
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
gdtSum: number
|
||||
|
||||
@Field(() => Number)
|
||||
count: number
|
||||
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
@Field(() => Number)
|
||||
decay: number
|
||||
|
||||
@Field(() => String)
|
||||
decayDate: string
|
||||
|
||||
@Field(() => [Transaction])
|
||||
transactions: Transaction[]
|
||||
}
|
||||
@ -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<GdtEntryList> {
|
||||
// load user
|
||||
|
||||
@ -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<boolean> {
|
||||
return await signIn(email, language)
|
||||
}
|
||||
|
||||
@ -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<Transaction[]> {
|
||||
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<TransactionList> {
|
||||
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<TransactionList> {
|
||||
// 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`, {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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<Transaction[]> {
|
||||
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<TransactionList> {
|
||||
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
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { EntityRepository, Repository } from 'typeorm'
|
||||
import { Order } from '../../graphql/enum/Order'
|
||||
import { UserTransaction } from '../entity/UserTransaction'
|
||||
|
||||
@EntityRepository(UserTransaction)
|
||||
@ -7,7 +8,7 @@ export class UserTransactionRepository extends Repository<UserTransaction> {
|
||||
userId: number,
|
||||
limit: number,
|
||||
offset: number,
|
||||
order: 'ASC' | 'DESC',
|
||||
order: Order,
|
||||
): Promise<[UserTransaction[], number]> {
|
||||
return this.createQueryBuilder('userTransaction')
|
||||
.where('userTransaction.userId = :userId', { userId })
|
||||
|
||||
@ -32,8 +32,8 @@ export const loginViaEmailVerificationCode = gql`
|
||||
`
|
||||
|
||||
export const transactionsQuery = gql`
|
||||
query($firstPage: Int = 1, $items: Int = 25, $order: String = "DESC") {
|
||||
transactionList(firstPage: $firstPage, items: $items, order: $order) {
|
||||
query($currentPage: Int = 1, $pageSize: Int = 25, $order: String = "DESC") {
|
||||
transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
|
||||
gdtSum
|
||||
count
|
||||
balance
|
||||
|
||||
@ -189,7 +189,7 @@ describe('DashboardLayoutGdd', () => {
|
||||
})
|
||||
await wrapper
|
||||
.findComponent({ ref: 'router-view' })
|
||||
.vm.$emit('update-transactions', { firstPage: 2, items: 5 })
|
||||
.vm.$emit('update-transactions', { currentPage: 2, pageSize: 5 })
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
@ -197,8 +197,8 @@ describe('DashboardLayoutGdd', () => {
|
||||
expect(apolloMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
firstPage: 2,
|
||||
items: 5,
|
||||
currentPage: 2,
|
||||
pageSize: 5,
|
||||
},
|
||||
}),
|
||||
)
|
||||
@ -233,7 +233,7 @@ describe('DashboardLayoutGdd', () => {
|
||||
})
|
||||
await wrapper
|
||||
.findComponent({ ref: 'router-view' })
|
||||
.vm.$emit('update-transactions', { firstPage: 2, items: 5 })
|
||||
.vm.$emit('update-transactions', { currentPage: 2, pageSize: 5 })
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
|
||||
@ -110,8 +110,8 @@ export default {
|
||||
.query({
|
||||
query: transactionsQuery,
|
||||
variables: {
|
||||
firstPage: pagination.firstPage,
|
||||
items: pagination.items,
|
||||
currentPage: pagination.currentPage,
|
||||
pageSize: pagination.pageSize,
|
||||
},
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
|
||||
@ -327,7 +327,9 @@ describe('GddTransactionList', () => {
|
||||
|
||||
it('emits update-transactions when next button is clicked', async () => {
|
||||
await paginationButtons.find('button.next-page').trigger('click')
|
||||
expect(wrapper.emitted('update-transactions')[1]).toEqual([{ firstPage: 2, items: 25 }])
|
||||
expect(wrapper.emitted('update-transactions')[1]).toEqual([
|
||||
{ currentPage: 2, pageSize: 25 },
|
||||
])
|
||||
})
|
||||
|
||||
it('shows text "2 / 2" when next button is clicked', async () => {
|
||||
@ -348,7 +350,9 @@ describe('GddTransactionList', () => {
|
||||
it('emits update-transactions when preivous button is clicked after next buton', async () => {
|
||||
await paginationButtons.find('button.next-page').trigger('click')
|
||||
await paginationButtons.find('button.previous-page').trigger('click')
|
||||
expect(wrapper.emitted('update-transactions')[2]).toEqual([{ firstPage: 1, items: 25 }])
|
||||
expect(wrapper.emitted('update-transactions')[2]).toEqual([
|
||||
{ currentPage: 1, pageSize: 25 },
|
||||
])
|
||||
expect(scrollToMock).toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@ -134,8 +134,8 @@ export default {
|
||||
methods: {
|
||||
updateTransactions() {
|
||||
this.$emit('update-transactions', {
|
||||
firstPage: this.currentPage,
|
||||
items: this.pageSize,
|
||||
currentPage: this.currentPage,
|
||||
pageSize: this.pageSize,
|
||||
})
|
||||
window.scrollTo(0, 0)
|
||||
},
|
||||
|
||||
@ -36,16 +36,16 @@ describe('UserProfileTransactionList', () => {
|
||||
|
||||
it('emits update-transactions after creation', () => {
|
||||
expect(wrapper.emitted('update-transactions')).toEqual(
|
||||
expect.arrayContaining([expect.arrayContaining([{ firstPage: 1, items: 25 }])]),
|
||||
expect.arrayContaining([expect.arrayContaining([{ currentPage: 1, pageSize: 25 }])]),
|
||||
)
|
||||
})
|
||||
|
||||
it('emist update-transactions when update-transactions is called', () => {
|
||||
wrapper
|
||||
.findComponent({ name: 'GddTransactionList' })
|
||||
.vm.$emit('update-transactions', { firstPage: 2, items: 25 })
|
||||
.vm.$emit('update-transactions', { currentPage: 2, pageSize: 25 })
|
||||
expect(wrapper.emitted('update-transactions')).toEqual(
|
||||
expect.arrayContaining([expect.arrayContaining([{ firstPage: 2, items: 25 }])]),
|
||||
expect.arrayContaining([expect.arrayContaining([{ currentPage: 2, pageSize: 25 }])]),
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user