lokaler zwischenstand

This commit is contained in:
Claus-Peter Huebner 2023-09-19 21:19:22 +02:00
parent a013fd241a
commit 4aeb1b60a2
9 changed files with 119 additions and 41 deletions

View File

@ -19,7 +19,7 @@ const constants = {
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
CONFIG_VERSION: {
DEFAULT: 'DEFAULT',
EXPECTED: 'v19.2023-09-01',
EXPECTED: 'v20.2023-09-19',
CURRENT: '',
},
}
@ -122,6 +122,7 @@ if (
}
const federation = {
FEDERATION_BACKEND_SEND_ON_API: process.env.FEDERATION_BACKEND_SEND_ON_API || '1_0',
FEDERATION_VALIDATE_COMMUNITY_TIMER:
Number(process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER) || 60000,
FEDERATION_XCOM_SENDCOINS_ENABLED:

View File

@ -5,6 +5,7 @@ import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
import { SendCoinsArgs } from './model/SendCoinsArgs'
import { SendCoinsResult } from './model/SendCoinsResult'
import { revertSendCoins } from './query/revertSendCoins'
import { revertSettledSendCoins } from './query/revertSettledSendCoins'
import { settleSendCoins } from './query/settleSendCoins'
@ -30,27 +31,30 @@ export class SendCoinsClient {
})
}
voteForSendCoins = async (args: SendCoinsArgs): Promise<string | undefined> => {
voteForSendCoins = async (args: SendCoinsArgs): Promise<SendCoinsResult> => {
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?.voteForSendCoins) {
if (!data?.voteForSendCoins?.vote) {
logger.warn(
'X-Com: voteForSendCoins failed with: ',
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
data.voteForSendCoins.voteForSendCoins,
data.voteForSendCoins.recipGradidoID,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
data.voteForSendCoins.recipName,
)
return
return new SendCoinsResult()
}
logger.debug(
'X-Com: voteForSendCoins successful with result=',
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
data.voteForSendCoins,
)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
return data.voteForSendCoins.voteForSendCoins
const result = new SendCoinsResult()
result.vote = true
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
result.recipGradidoID = data.voteForSendCoins.recipGradidoID
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
result.recipName = data.voteForSendCoins.recipName
logger.debug('X-Com: voteForSendCoins successful with result=', result)
return result
} catch (err) {
throw new LogError(`X-Com: voteForSendCoins failed for endpoint=${this.endpoint}:`, err)
}

View File

@ -9,9 +9,9 @@ export class SendCoinsResult {
@Field(() => Boolean)
vote: boolean
@Field(() => String)
receiverFirstName: string
@Field(() => String, { nullable: true })
recipGradidoID: string | null
@Field(() => String)
receiverLastName: string
@Field(() => String, { nullable: true })
recipName: string | null
}

View File

@ -20,6 +20,10 @@ export const voteForSendCoins = gql`
senderCommunityUuid: $senderCommunityUuid
senderUserUuid: $senderUserUuid
senderUserName: $senderUserName
)
) {
vote
recipGradidoID
recipName
}
}
`

View File

@ -58,7 +58,7 @@ let bob: User
let peter: User
let homeCom: DbCommunity
// let foreignCom: DbCommunity
let foreignCom: DbCommunity
describe('send coins', () => {
beforeAll(async () => {
@ -67,7 +67,7 @@ describe('send coins', () => {
await userFactory(testEnv, stephenHawking)
await userFactory(testEnv, garrickOllivander)
homeCom = DbCommunity.create()
homeCom.communityUuid = 'homeCom-UUID'
homeCom.communityUuid = '7f474922-b6d8-4b64-8cd0-ebf0a1d8756e'
homeCom.creationDate = new Date('2000-01-01')
homeCom.description = 'homeCom description'
homeCom.foreign = false
@ -77,6 +77,17 @@ describe('send coins', () => {
homeCom.url = 'homeCom url'
homeCom = await DbCommunity.save(homeCom)
foreignCom = DbCommunity.create()
foreignCom.communityUuid = '7f474922-b6d8-4b64-8cd0-cea0a1d8756e'
foreignCom.creationDate = new Date('2000-06-06')
foreignCom.description = 'homeCom description'
foreignCom.foreign = true
foreignCom.name = 'foreignCom name'
foreignCom.privateKey = Buffer.from('foreignCom privateKey')
foreignCom.publicKey = Buffer.from('foreignCom publicKey')
foreignCom.url = 'foreignCom url'
foreignCom = await DbCommunity.save(foreignCom)
bobData = {
email: 'bob@baumeister.de',
password: 'Aa12345_',
@ -583,6 +594,27 @@ describe('send coins', () => {
})
})
describe.only('X-Com send coins via gradido ID', () => {
it('sends the coins', async () => {
await expect(
mutate({
mutation: sendCoins,
variables: {
recipientCommunityIdentifier: foreignCom.communityUuid,
recipientIdentifier: peter?.gradidoID,
amount: 10,
memo: 'x-com send via gradido ID',
},
}),
).resolves.toMatchObject({
data: {
sendCoins: true,
},
errors: undefined,
})
})
})
describe('more transactions to test semaphore', () => {
it('sends the coins four times in a row', async () => {
await expect(

View File

@ -43,6 +43,7 @@ import { getLastTransaction } from './util/getLastTransaction'
import { getTransactionList } from './util/getTransactionList'
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
import { transactionLinkSummary } from './util/transactionLinkSummary'
import { processXComPendingSendCoins } from './util/processXComSendCoins'
export const executeTransaction = async (
amount: Decimal,
@ -545,6 +546,17 @@ export class TransactionResolver {
if (!(await isCommunityAuthenticated(recipientCommunityIdentifier))) {
throw new LogError('recipient commuity is connected, but still not authenticated yet!')
}
const recipCom = await dbCommunity.findOneOrFail({
where: { communityUuid: recipientCommunityIdentifier },
})
await processXComPendingSendCoins(
recipCom,
homeCom,
amount,
memo,
senderUser,
recipientIdentifier,
)
}
return true
}

View File

@ -19,27 +19,24 @@ import { fullName } from '@/util/utilities'
import { settlePendingSenderTransaction } from './settlePendingSenderTransaction'
export async function processXComPendingSendCoins(
receiverFCom: DbFederatedCommunity,
receiverCom: DbCommunity,
senderCom: DbCommunity,
creationDate: Date,
amount: Decimal,
memo: string,
sender: dbUser,
recipient: dbUser,
recipientIdentifier: string,
): Promise<boolean> {
try {
logger.debug(
`XCom: processXComPendingSendCoins...`,
receiverFCom,
receiverCom,
senderCom,
creationDate,
amount,
memo,
sender,
recipient,
recipientIdentifier,
)
const creationDate = new Date()
// first calculate the sender balance and check if the transaction is allowed
const senderBalance = await calculateSenderBalance(sender.id, amount.mul(-1), creationDate)
if (!senderBalance) {
@ -47,24 +44,32 @@ export async function processXComPendingSendCoins(
}
logger.debug(`X-Com: calculated senderBalance = `, senderBalance)
const receiverFCom = await DbFederatedCommunity.findOneOrFail({
where: {
publicKey: receiverCom.publicKey,
apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API,
},
})
const client = SendCoinsClientFactory.getInstance(receiverFCom)
// eslint-disable-next-line camelcase
if (client instanceof V1_0_SendCoinsClient) {
const args = new SendCoinsArgs()
args.recipientCommunityUuid = receiverCom.communityUuid
? receiverCom.communityUuid
: CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID
args.recipientUserIdentifier = recipient.gradidoID
if (receiverCom.communityUuid) {
args.recipientCommunityUuid = receiverCom.communityUuid
}
args.recipientUserIdentifier = recipientIdentifier
args.creationDate = creationDate.toISOString()
args.amount = amount
args.memo = memo
args.senderCommunityUuid = senderCom.communityUuid ? senderCom.communityUuid : 'homeCom-UUID'
if (senderCom.communityUuid) {
args.senderCommunityUuid = senderCom.communityUuid
}
args.senderUserUuid = sender.gradidoID
args.senderUserName = fullName(sender.firstName, sender.lastName)
logger.debug(`X-Com: ready for voteForSendCoins with args=`, args)
const recipientName = await client.voteForSendCoins(args)
logger.debug(`X-Com: returnd from voteForSendCoins:`, recipientName)
if (recipientName) {
const sendCoinsResult = await client.voteForSendCoins(args)
logger.debug(`X-Com: returnd from voteForSendCoins:`, sendCoinsResult)
if (sendCoinsResult.vote) {
// writing the pending transaction on receiver-side was successfull, so now write the sender side
try {
const pendingTx = DbPendingTransaction.create()
@ -73,11 +78,11 @@ export async function processXComPendingSendCoins(
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 = recipientName
if (receiverCom.communityUuid) {
pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid
}
pendingTx.linkedUserGradidoID = sendCoinsResult.recipGradidoID
pendingTx.linkedUserName = sendCoinsResult.recipName
pendingTx.memo = memo
pendingTx.previous = senderBalance ? senderBalance.lastTransactionId : null
pendingTx.state = PendingTransactionState.NEW

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, { nullable: true })
recipGradidoID: string | null
@Field(() => String, { nullable: true })
recipName: string | null
}

View File

@ -14,6 +14,7 @@ import { settlePendingReceiveTransaction } from '../util/settlePendingReceiveTra
// import { checkTradingLevel } from '@/graphql/util/checkTradingLevel'
import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTransaction'
import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier'
import { SendCoinsResult } from '../model/SendCoinsResult'
@Resolver()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -31,7 +32,7 @@ export class SendCoinsResolver {
senderUserUuid,
senderUserName,
}: SendCoinsArgs,
): Promise<string> {
): Promise<SendCoinsResult> {
logger.debug(
`voteForSendCoins() via apiVersion=1_0 ...`,
recipientCommunityUuid,
@ -43,7 +44,7 @@ export class SendCoinsResolver {
senderUserUuid,
senderUserName,
)
let result: string
const result = new SendCoinsResult()
// first check if receiver community is correct
const homeCom = await DbCommunity.findOneBy({
communityUuid: recipientCommunityUuid,
@ -88,7 +89,9 @@ export class SendCoinsResolver {
pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName)
await DbPendingTransaction.insert(pendingTx)
result = pendingTx.userName
result.vote = true
result.recipName = pendingTx.userName
result.recipGradidoID = pendingTx.userGradidoID
logger.debug(`voteForSendCoins()-1_0... successfull`)
} catch (err) {
throw new LogError(`Error in voteForSendCoins: `, err)