first draft of write senderPendingTX

This commit is contained in:
Claus-Peter Huebner 2023-08-25 03:21:18 +02:00
parent 7d8b97b861
commit 48af8a7a90
7 changed files with 157 additions and 11 deletions

View File

@ -19,7 +19,7 @@ const constants = {
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v18.2023-07-10',
EXPECTED: 'v19.2023-08-25',
CURRENT: '',
},
}
@ -124,6 +124,9 @@ if (
const federation = {
FEDERATION_VALIDATE_COMMUNITY_TIMER:
Number(process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER) || 60000,
// default value for community-uuid is equal uuid of stage-3
FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID:
process.env.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID ?? '56a55482-909e-46a4-bfa2-cd025e894ebc',
}
export const CONFIG = {

View File

@ -6,6 +6,7 @@ import { backendLogger as logger } from '@/server/logger'
import { SendCoinsArgs } from './model/SendCoinsArgs'
import { voteForSendCoins } from './query/voteForSendCoins'
import { SendCoinsResult } from './model/SendCoinsResult'
// eslint-disable-next-line camelcase
export class SendCoinsClient {
@ -27,23 +28,25 @@ export class SendCoinsClient {
})
}
voteForSendCoins = async (args: SendCoinsArgs): Promise<boolean> => {
voteForSendCoins = async (args: SendCoinsArgs): Promise<SendCoinsResult | null> => {
logger.debug('X-Com: voteForSendCoins against endpoint', this.endpoint)
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { data } = await this.client.rawRequest(voteForSendCoins, { args })
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (!data?.voteForSendCoins) {
logger.warn('X-Com: voteForSendCoins without response data from endpoint', this.endpoint)
return false
if (!data?.voteForSendCoins?.SendCoinsResult.vote) {
logger.warn(
'X-Com: voteForSendCoins failed with: ',
data?.voteForSendCoins?.SendCoinsResult,
)
return null
}
logger.debug(
'X-Com: voteForSendCoins successful from endpoint',
this.endpoint,
'X-Com: voteForSendCoins successful with result=',
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
data.voteForSendCoins.vote,
data.voteForSendCoins.SendCoinsResult,
)
return true
return data.voteForSendCoins.SendCoinsResult
} catch (err) {
throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err)
}

View File

@ -0,0 +1,17 @@
import { ArgsType, Field } from 'type-graphql'
@ArgsType()
export class SendCoinsResult {
constructor() {
this.vote = false
}
@Field(() => Boolean)
vote: boolean
@Field(() => String)
receiverFirstName: string
@Field(() => String)
receiverLastName: string
}

View File

@ -0,0 +1,80 @@
import { Community as DbCommunity } from '@entity/Community'
import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity'
import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction'
import { User as dbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs'
// eslint-disable-next-line camelcase
import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient'
import { SendCoinsClientFactory } from '@/federation/client/SendCoinsClientFactory'
import { backendLogger as logger } from '@/server/logger'
import { CONFIG } from '@/config'
import { fullName } from '@/util/utilities'
import { calculateSenderBalance } from '@/util/calculateSenderBalance'
import { LogError } from '@/server/LogError'
export async function processXComSendCoins(
receiverFCom: DbFederatedCommunity,
senderFCom: DbFederatedCommunity,
receiverCom: DbCommunity,
senderCom: DbCommunity,
creationDate: Date,
amount: Decimal,
memo: string,
sender: dbUser,
recipient: dbUser,
): Promise<boolean> {
try {
const senderBalance = await calculateSenderBalance(sender.id, amount.mul(-1), creationDate)
if (!senderBalance) {
throw new LogError('User has not enough GDD or amount is < 0', senderBalance)
}
const client = SendCoinsClientFactory.getInstance(receiverFCom)
// eslint-disable-next-line camelcase
if (client instanceof V1_0_SendCoinsClient) {
const args = new SendCoinsArgs()
args.communityReceiverIdentifier = receiverCom.communityUuid
? receiverCom.communityUuid
: CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID
args.userReceiverIdentifier = recipient.gradidoID
args.creationDate = creationDate
args.amount = amount
args.memo = memo
args.communitySenderIdentifier = senderCom.communityUuid
? senderCom.communityUuid
: 'homeCom-UUID'
args.userSenderIdentifier = sender.gradidoID
args.userSenderName = fullName(sender.firstName, sender.lastName)
const result = await client.voteForSendCoins(args)
if(result) {
const pendingTx = DbPendingTransaction.create()
pendingTx.amount = amount.mul(-1)
pendingTx.balance = senderBalance ? senderBalance.balance : new Decimal(0)
pendingTx.balanceDate = creationDate
pendingTx.decay = senderBalance ? senderBalance.decay.decay : new Decimal(0)
pendingTx.decayStart = senderBalance ? senderBalance.decay.start : null
pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid
? receiverCom.communityUuid
: CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID
pendingTx.linkedUserGradidoID = recipient.gradidoID
pendingTx.linkedUserName = userSenderName
pendingTx.memo = memo
pendingTx.previous = receiveBalance ? receiveBalance.lastTransactionId : null
pendingTx.state = PendingTransactionState.NEW
pendingTx.typeId = TransactionTypeId.RECEIVE
pendingTx.userCommunityUuid = communityReceiverIdentifier
pendingTx.userGradidoID = userReceiverIdentifier
pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName)
await DbPendingTransaction.insert(pendingTx)
logger.debug(`voteForSendCoins()-1_0... successfull`)
}
}
} catch (err) {
logger.error(`Error:`, err)
}
return true
}

View File

@ -0,0 +1,21 @@
import { Decimal } from 'decimal.js-light'
import { Decay } from '@model/Decay'
import { getLastTransaction } from '@/graphql/resolver/util/getLastTransaction'
import { calculateDecay } from './decay'
export async function calculateSenderBalance(
userId: number,
amount: Decimal,
time: Date,
): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number } | null> {
const lastTransaction = await getLastTransaction(userId)
if (!lastTransaction) return null
const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)
const balance = decay.balance.add(amount.toString())
return { balance, lastTransactionId: lastTransaction.id, decay }
}

View File

@ -0,0 +1,17 @@
import { ArgsType, Field } from 'type-graphql'
@ArgsType()
export class SendCoinsResult {
constructor() {
this.vote = false
}
@Field(() => Boolean)
vote: boolean
@Field(() => String)
receiverFirstName: string
@Field(() => String)
receiverLastName: string
}

View File

@ -11,6 +11,7 @@ import { TransactionTypeId } from '../enum/TransactionTypeId'
import { calculateRecepientBalance } from '@/graphql/util/calculateRecepientBalance'
import Decimal from 'decimal.js-light'
import { fullName } from '@/graphql/util/fullName'
import { SendCoinsResult } from '../model/SendCoinsResult'
@Resolver()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -28,7 +29,8 @@ export class SendCoinsResolver {
userSenderIdentifier,
userSenderName,
}: SendCoinsArgs,
): Promise<boolean> {
): Promise<SendCoinsResult> {
const result = new SendCoinsResult()
logger.debug(`voteForSendCoins() via apiVersion=1_0 ...`)
try {
// first check if receiver community is correct
@ -68,10 +70,13 @@ export class SendCoinsResolver {
pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName)
await DbPendingTransaction.insert(pendingTx)
result.vote = true
result.receiverFirstName = receiverUser.firstName
result.receiverLastName = receiverUser.lastName
logger.debug(`voteForSendCoins()-1_0... successfull`)
} catch (err) {
throw new LogError(`Error in voteForSendCoins: `, err)
}
return true
return result
}
}