From 0c72fae9a23d86ec5bf5bd4de42f48f72221b188 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 17 Aug 2023 14:09:15 +0200 Subject: [PATCH 01/24] first steps of starting X-sendCoins --- .../src/graphql/arg/TransactionSendArgs.ts | 3 ++ .../graphql/resolver/TransactionResolver.ts | 39 ++++++++++++------- .../src/graphql/resolver/util/communities.ts | 19 +++++++++ frontend/src/graphql/mutations.js | 2 +- 4 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 backend/src/graphql/resolver/util/communities.ts diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index ecda848d1..010c71183 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -3,6 +3,9 @@ import { ArgsType, Field } from 'type-graphql' @ArgsType() export class TransactionSendArgs { + @Field(() => String) + communityIdentifier: string + @Field(() => String) identifier: string diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index ba5d6e155..2cf6abb0d 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -34,6 +34,7 @@ import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualT import { BalanceResolver } from './BalanceResolver' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' +import { isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -317,24 +318,32 @@ export class TransactionResolver { @Authorized([RIGHTS.SEND_COINS]) @Mutation(() => Boolean) async sendCoins( - @Args() { identifier, amount, memo }: TransactionSendArgs, + @Args() { communityIdentifier, identifier, amount, memo }: TransactionSendArgs, @Ctx() context: Context, ): Promise { - logger.info(`sendCoins(identifier=${identifier}, amount=${amount}, memo=${memo})`) - if (amount.lte(0)) { - throw new LogError('Amount to send must be positive', amount) + logger.info( + `sendCoins(communityIdentifier=${communityIdentifier}, identifier=${identifier}, amount=${amount}, memo=${memo})`, + ) + if (!communityIdentifier || (await isHomeCommunity(communityIdentifier))) { + // processing a local sendCoins + if (amount.lte(0)) { + throw new LogError('Amount to send must be positive', amount) + } + + const senderUser = getUser(context) + + // validate recipient user + const recipientUser = await findUserByIdentifier(identifier) + if (!recipientUser) { + throw new LogError('The recipient user was not found', recipientUser) + } + + await executeTransaction(amount, memo, senderUser, recipientUser) + logger.info('successful executeTransaction', amount, memo, senderUser, recipientUser) + } else { + // processing a x-community sendCoins + logger.debug('processing a x-community transaction...') } - - const senderUser = getUser(context) - - // validate recipient user - const recipientUser = await findUserByIdentifier(identifier) - if (!recipientUser) { - throw new LogError('The recipient user was not found', recipientUser) - } - - await executeTransaction(amount, memo, senderUser, recipientUser) - logger.info('successful executeTransaction', amount, memo, senderUser, recipientUser) return true } } diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts new file mode 100644 index 000000000..49a3f7744 --- /dev/null +++ b/backend/src/graphql/resolver/util/communities.ts @@ -0,0 +1,19 @@ +import { Community as DbCommunity } from '@entity/Community' + +export async function isHomeCommunity(communityIdentifier: string): Promise { + const homeCommunity = await DbCommunity.findOneByOrFail({ foreign: false }) + if (communityIdentifier === homeCommunity.name) { + return true + } else if (communityIdentifier === homeCommunity.communityUuid) { + return true + } else if (communityIdentifier === homeCommunity.url) { + return true + } else { + return false + } +} + +export async function getCommunityUrl(communityIdentifier: string): Promise { + const community = await DbCommunity.findOneByOrFail({ name: communityIdentifier }) + return community.url +} diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 2f6b53ac9..2065d92a8 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -72,7 +72,7 @@ export const createUser = gql` export const sendCoins = gql` mutation($identifier: String!, $amount: Decimal!, $memo: String!) { - sendCoins(identifier: $identifier, amount: $amount, memo: $memo) + sendCoins(communityIdentifier: $communityIdentifier, identifier: $identifier, amount: $amount, memo: $memo) } ` From 77128f85a323caa1478116c4be5ca7f7a52a9584 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 17 Aug 2023 19:12:51 +0200 Subject: [PATCH 02/24] save the work --- .../src/graphql/arg/TransactionSendArgs.ts | 6 +- .../graphql/resolver/TransactionResolver.ts | 170 +++++++++++++++++- backend/src/seeds/graphql/mutations.ts | 9 +- frontend/src/graphql/mutations.js | 9 +- 4 files changed, 179 insertions(+), 15 deletions(-) diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index 010c71183..aa1313350 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -3,9 +3,6 @@ import { ArgsType, Field } from 'type-graphql' @ArgsType() export class TransactionSendArgs { - @Field(() => String) - communityIdentifier: string - @Field(() => String) identifier: string @@ -14,4 +11,7 @@ export class TransactionSendArgs { @Field(() => String) memo: string + + @Field(() => String) + communityIdentifier: string } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 2cf6abb0d..8398abe91 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -191,6 +191,158 @@ export const executeTransaction = async ( return true } +/* +export const executeCrossTransaction = async ( + amount: Decimal, + memo: string, + sender: dbUser, + recipientIdentifier: string, + transactionLink?: dbTransactionLink | null, +): Promise => { + // acquire lock + const releaseLock = await TRANSACTIONS_LOCK.acquire() + try { + logger.info('executeCrossTransaction', amount, memo, sender, recipientIdentifier) + + if (sender.id === recipient.id) { + throw new LogError('Sender and Recipient are the same', sender.id) + } + + if (memo.length < MEMO_MIN_CHARS) { + throw new LogError('Memo text is too short', memo.length) + } + + if (memo.length > MEMO_MAX_CHARS) { + throw new LogError('Memo text is too long', memo.length) + } + + // validate amount + const receivedCallDate = new Date() + const sendBalance = await calculateBalance( + sender.id, + amount.mul(-1), + receivedCallDate, + transactionLink, + ) + logger.debug(`calculated Balance=${sendBalance}`) + if (!sendBalance) { + throw new LogError('User has not enough GDD or amount is < 0', sendBalance) + } + + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() + await queryRunner.startTransaction('REPEATABLE READ') + logger.debug(`open Transaction to write...`) + try { + // transaction + const transactionSend = new dbTransaction() + transactionSend.typeId = TransactionTypeId.SEND + transactionSend.memo = memo + transactionSend.userId = sender.id + transactionSend.userGradidoID = sender.gradidoID + transactionSend.userName = fullName(sender.firstName, sender.lastName) + transactionSend.linkedUserId = recipient.id + transactionSend.linkedUserGradidoID = recipient.gradidoID + transactionSend.linkedUserName = fullName(recipient.firstName, recipient.lastName) + transactionSend.amount = amount.mul(-1) + transactionSend.balance = sendBalance.balance + transactionSend.balanceDate = receivedCallDate + transactionSend.decay = sendBalance.decay.decay + transactionSend.decayStart = sendBalance.decay.start + transactionSend.previous = sendBalance.lastTransactionId + transactionSend.transactionLinkId = transactionLink ? transactionLink.id : null + await queryRunner.manager.insert(dbTransaction, transactionSend) + + logger.debug(`sendTransaction inserted: ${dbTransaction}`) + + const transactionReceive = new dbTransaction() + transactionReceive.typeId = TransactionTypeId.RECEIVE + transactionReceive.memo = memo + transactionReceive.userId = recipient.id + transactionReceive.userGradidoID = recipient.gradidoID + transactionReceive.userName = fullName(recipient.firstName, recipient.lastName) + transactionReceive.linkedUserId = sender.id + transactionReceive.linkedUserGradidoID = sender.gradidoID + transactionReceive.linkedUserName = fullName(sender.firstName, sender.lastName) + transactionReceive.amount = amount + const receiveBalance = await calculateBalance(recipient.id, amount, receivedCallDate) + transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount + transactionReceive.balanceDate = receivedCallDate + transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) + transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null + transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null + transactionReceive.linkedTransactionId = transactionSend.id + transactionReceive.transactionLinkId = transactionLink ? transactionLink.id : null + await queryRunner.manager.insert(dbTransaction, transactionReceive) + logger.debug(`receive Transaction inserted: ${dbTransaction}`) + + // Save linked transaction id for send + transactionSend.linkedTransactionId = transactionReceive.id + await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) + logger.debug('send Transaction updated', transactionSend) + + if (transactionLink) { + logger.info('transactionLink', transactionLink) + transactionLink.redeemedAt = receivedCallDate + transactionLink.redeemedBy = recipient.id + await queryRunner.manager.update( + dbTransactionLink, + { id: transactionLink.id }, + transactionLink, + ) + } + + await queryRunner.commitTransaction() + logger.info(`commit Transaction successful...`) + + await EVENT_TRANSACTION_SEND(sender, recipient, transactionSend, transactionSend.amount) + + await EVENT_TRANSACTION_RECEIVE( + recipient, + sender, + transactionReceive, + transactionReceive.amount, + ) + + // trigger to send transaction via dlt-connector + void sendTransactionsToDltConnector() + } catch (e) { + await queryRunner.rollbackTransaction() + throw new LogError('Transaction was not successful', e) + } finally { + await queryRunner.release() + } + void sendTransactionReceivedEmail({ + firstName: recipient.firstName, + lastName: recipient.lastName, + email: recipient.emailContact.email, + language: recipient.language, + senderFirstName: sender.firstName, + senderLastName: sender.lastName, + senderEmail: sender.emailContact.email, + transactionAmount: amount, + }) + if (transactionLink) { + void sendTransactionLinkRedeemedEmail({ + firstName: sender.firstName, + lastName: sender.lastName, + email: sender.emailContact.email, + language: sender.language, + senderFirstName: recipient.firstName, + senderLastName: recipient.lastName, + senderEmail: recipient.emailContact.email, + transactionAmount: amount, + transactionMemo: memo, + }) + } + logger.info(`finished executeTransaction successfully`) + } finally { + releaseLock() + } + return true +} +*/ + @Resolver() export class TransactionResolver { @Authorized([RIGHTS.TRANSACTION_LIST]) @@ -318,19 +470,21 @@ export class TransactionResolver { @Authorized([RIGHTS.SEND_COINS]) @Mutation(() => Boolean) async sendCoins( - @Args() { communityIdentifier, identifier, amount, memo }: TransactionSendArgs, + @Args() { identifier, amount, memo, communityIdentifier }: TransactionSendArgs, @Ctx() context: Context, ): Promise { logger.info( - `sendCoins(communityIdentifier=${communityIdentifier}, identifier=${identifier}, amount=${amount}, memo=${memo})`, + `sendCoins(identifier=${identifier}, amount=${amount}, memo=${memo}, communityIdentifier=${communityIdentifier})`, ) - if (!communityIdentifier || (await isHomeCommunity(communityIdentifier))) { - // processing a local sendCoins - if (amount.lte(0)) { - throw new LogError('Amount to send must be positive', amount) - } - const senderUser = getUser(context) + if (amount.lte(0)) { + throw new LogError('Amount to send must be positive', amount) + } + + const senderUser = getUser(context) + + if (!communityIdentifier || (await isHomeCommunity(communityIdentifier))) { + // processing sendCoins within sender and recepient are both in home community // validate recipient user const recipientUser = await findUserByIdentifier(identifier) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 87231531f..f2f9937a0 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -79,8 +79,13 @@ export const sendActivationEmail = gql` ` export const sendCoins = gql` - mutation ($identifier: String!, $amount: Decimal!, $memo: String!) { - sendCoins(identifier: $identifier, amount: $amount, memo: $memo) + mutation ($identifier: String!, $amount: Decimal!, $memo: String!, $communityIdentifier: String) { + sendCoins( + identifier: $identifier + amount: $amount + memo: $memo + communityIdentifier: $communityIdentifier + ) } ` diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 2065d92a8..2efed09fd 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -71,8 +71,13 @@ export const createUser = gql` ` export const sendCoins = gql` - mutation($identifier: String!, $amount: Decimal!, $memo: String!) { - sendCoins(communityIdentifier: $communityIdentifier, identifier: $identifier, amount: $amount, memo: $memo) + mutation ($identifier: String!, $amount: Decimal!, $memo: String!, $communityIdentifier: String) { + sendCoins( + identifier: $identifier + amount: $amount + memo: $memo + communityIdentifier: $communityIdentifier + ) } ` From 89e106ae6d996a5b3c4b6ded36b36d69722109ec Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 1 Sep 2023 00:31:33 +0200 Subject: [PATCH 03/24] linting --- backend/src/graphql/resolver/TransactionResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 6cd31adb9..57307cba4 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -33,7 +33,6 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' import { isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' From 47ea0fdb9b2a0ae4f2a9ac51c8e5745f711df1cf Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 1 Sep 2023 01:41:38 +0200 Subject: [PATCH 04/24] activate recipientCommunityIdentifier for sendCoins api --- .../src/graphql/arg/TransactionSendArgs.ts | 3 -- .../resolver/TransactionResolver.test.ts | 28 +++++++++++++++++++ .../src/graphql/resolver/semaphore.test.ts | 15 ++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/arg/TransactionSendArgs.ts b/backend/src/graphql/arg/TransactionSendArgs.ts index 9d6842bdc..48827be8d 100644 --- a/backend/src/graphql/arg/TransactionSendArgs.ts +++ b/backend/src/graphql/arg/TransactionSendArgs.ts @@ -23,7 +23,4 @@ export class TransactionSendArgs { @MaxLength(MEMO_MAX_CHARS) @MinLength(MEMO_MIN_CHARS) memo: string - - @Field(() => String) - communityIdentifier: string } diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index bd7d0f2a8..0f4171dd4 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { Connection, In } from '@dbTools/typeorm' +import { Community as DbCommunity } from '@entity/Community' import { DltTransaction } from '@entity/DltTransaction' import { Event as DbEvent } from '@entity/Event' import { Transaction } from '@entity/Transaction' @@ -56,12 +57,24 @@ let user: User[] let bob: User let peter: User +let homeCom: DbCommunity + describe('send coins', () => { beforeAll(async () => { peter = await userFactory(testEnv, peterLustig) bob = await userFactory(testEnv, bobBaumeister) await userFactory(testEnv, stephenHawking) await userFactory(testEnv, garrickOllivander) + homeCom = DbCommunity.create() + homeCom.communityUuid = 'homeCom-UUID' + homeCom.creationDate = new Date('2000-01-01') + homeCom.description = 'homeCom description' + homeCom.foreign = false + homeCom.name = 'homeCom name' + homeCom.privateKey = Buffer.from('homeCom privateKey') + homeCom.publicKey = Buffer.from('homeCom publicKey') + homeCom.url = 'homeCom url' + homeCom = await DbCommunity.save(homeCom) bobData = { email: 'bob@baumeister.de', @@ -91,6 +104,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'wrong@email.com', amount: 100, memo: 'test test', @@ -119,6 +133,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'stephen@hawking.uk', amount: 100, memo: 'test test', @@ -148,6 +163,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'garrick@ollivander.com', amount: 100, memo: 'test test', @@ -184,6 +200,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'bob@baumeister.de', amount: 100, memo: 'test test', @@ -207,6 +224,7 @@ describe('send coins', () => { const { errors: errorObjects } = await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 100, memo: 'Test', @@ -238,6 +256,7 @@ describe('send coins', () => { const { errors: errorObjects } = await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 100, memo: 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test t', @@ -270,6 +289,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 100, memo: 'testing', @@ -319,6 +339,7 @@ describe('send coins', () => { const { errors: errorObjects } = await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: -50, memo: 'testing negative', @@ -350,6 +371,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 50, memo: 'unrepeatable memo', @@ -456,6 +478,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: peter?.gradidoID, amount: 10, memo: 'send via gradido ID', @@ -496,6 +519,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'bob', amount: 6.66, memo: 'send via alias', @@ -564,6 +588,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 10, memo: 'first transaction', @@ -580,6 +605,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 20, memo: 'second transaction', @@ -596,6 +622,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 30, memo: 'third transaction', @@ -612,6 +639,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 40, memo: 'fourth transaction', diff --git a/backend/src/graphql/resolver/semaphore.test.ts b/backend/src/graphql/resolver/semaphore.test.ts index 37331d832..dc6c5b364 100644 --- a/backend/src/graphql/resolver/semaphore.test.ts +++ b/backend/src/graphql/resolver/semaphore.test.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ import { Connection } from '@dbTools/typeorm' +import { Community as DbCommunity } from '@entity/Community' import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { GraphQLError } from 'graphql' @@ -48,9 +49,21 @@ describe('semaphore', () => { let bibisTransactionLinkCode = '' let bibisOpenContributionId = -1 let bobsOpenContributionId = -1 + let homeCom: DbCommunity beforeAll(async () => { const now = new Date() + homeCom = DbCommunity.create() + homeCom.communityUuid = 'homeCom-UUID' + homeCom.creationDate = new Date('2000-01-01') + homeCom.description = 'homeCom description' + homeCom.foreign = false + homeCom.name = 'homeCom name' + homeCom.privateKey = Buffer.from('homeCom privateKey') + homeCom.publicKey = Buffer.from('homeCom publicKey') + homeCom.url = 'homeCom url' + homeCom = await DbCommunity.save(homeCom) + await userFactory(testEnv, bibiBloxberg) await userFactory(testEnv, peterLustig) await userFactory(testEnv, bobBaumeister) @@ -157,6 +170,7 @@ describe('semaphore', () => { const bibisTransaction = mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'bob@baumeister.de', amount: '50', memo: 'Das ist für dich, Bob', @@ -177,6 +191,7 @@ describe('semaphore', () => { const bobsTransaction = mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'bibi@bloxberg.de', amount: '50', memo: 'Das ist für dich, Bibi', From a16c1fa99110a9345dd3d5b98ec5fa205ed9549e Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 1 Sep 2023 02:25:15 +0200 Subject: [PATCH 05/24] add config-switch and authentication-check before starting X-Com-sendCoins --- backend/src/config/index.ts | 4 +++- .../graphql/resolver/TransactionResolver.test.ts | 1 + .../src/graphql/resolver/TransactionResolver.ts | 9 ++++++++- backend/src/graphql/resolver/util/communities.ts | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 744f1d3cc..c96f60d17 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -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-09-01', CURRENT: '', }, } @@ -124,6 +124,8 @@ if ( const federation = { FEDERATION_VALIDATE_COMMUNITY_TIMER: Number(process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER) || 60000, + FEDERATION_XCOM_SENDCOINS_ENABLED: + process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' || false, } export const CONFIG = { diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 0f4171dd4..ab5558048 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -58,6 +58,7 @@ let bob: User let peter: User let homeCom: DbCommunity +let foreignCom: DbCommunity describe('send coins', () => { beforeAll(async () => { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 57307cba4..558f2beb4 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -33,12 +33,13 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { isHomeCommunity } from './util/communities' +import { isCommunityAuthenticated, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' import { transactionLinkSummary } from './util/transactionLinkSummary' +import { CONFIG } from '@/config' export const executeTransaction = async ( amount: Decimal, @@ -484,6 +485,12 @@ export class TransactionResolver { } else { // processing a x-community sendCoins logger.debug('processing a x-community transaction...') + if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { + throw new LogError('X-Community sendCoins disabled per configuration!') + } + if (!(await isCommunityAuthenticated(recipientCommunityIdentifier))) { + throw new LogError('recipient commuity is connected, but still not authenticated yet!') + } } return true } diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index e4ceeb54b..59e79f077 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -19,3 +19,18 @@ export async function getCommunityUrl(communityIdentifier: string): Promise { + const community = await DbCommunity.findOneOrFail({ + where: [ + { communityUuid: communityIdentifier }, + { name: communityIdentifier }, + { url: communityIdentifier }, + ], + }) + if (community.authenticatedAt) { + return true + } else { + return false + } +} From c3d821bb09955efe00cf5ccd0b3b930447a336ef Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 6 Sep 2023 17:47:20 +0200 Subject: [PATCH 06/24] linting --- backend/src/graphql/resolver/TransactionResolver.test.ts | 2 +- backend/src/graphql/resolver/TransactionResolver.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index ab5558048..a59ca5c0a 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -58,7 +58,7 @@ let bob: User let peter: User let homeCom: DbCommunity -let foreignCom: DbCommunity +// let foreignCom: DbCommunity describe('send coins', () => { beforeAll(async () => { diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index eee222d87..e860b50a1 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -18,6 +18,7 @@ import { TransactionList } from '@model/TransactionList' import { User } from '@model/User' import { RIGHTS } from '@/auth/RIGHTS' +import { CONFIG } from '@/config' import { sendTransactionLinkRedeemedEmail, sendTransactionReceivedEmail, @@ -39,7 +40,6 @@ import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' import { transactionLinkSummary } from './util/transactionLinkSummary' -import { CONFIG } from '@/config' export const executeTransaction = async ( amount: Decimal, From 4aeb1b60a225413484691afd222c04e926c3507e Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Tue, 19 Sep 2023 21:19:22 +0200 Subject: [PATCH 07/24] lokaler zwischenstand --- backend/src/config/index.ts | 3 +- .../federation/client/1_0/SendCoinsClient.ts | 26 ++++++----- .../client/1_0/model/SendCoinsResult.ts | 8 ++-- .../client/1_0/query/voteForSendCoins.ts | 6 ++- .../resolver/TransactionResolver.test.ts | 36 +++++++++++++++- .../graphql/resolver/TransactionResolver.ts | 12 ++++++ .../resolver/util/processXComSendCoins.ts | 43 +++++++++++-------- .../graphql/api/1_0/model/SendCoinsResult.ts | 17 ++++++++ .../api/1_0/resolver/SendCoinsResolver.ts | 9 ++-- 9 files changed, 119 insertions(+), 41 deletions(-) create mode 100644 federation/src/graphql/api/1_0/model/SendCoinsResult.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 07e2ded36..98a0b8323 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -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: diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 7f1fad186..f9c318e81 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -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 => { + voteForSendCoins = async (args: SendCoinsArgs): Promise => { 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) } diff --git a/backend/src/federation/client/1_0/model/SendCoinsResult.ts b/backend/src/federation/client/1_0/model/SendCoinsResult.ts index 1897410cc..3eb1419b5 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsResult.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsResult.ts @@ -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 } diff --git a/backend/src/federation/client/1_0/query/voteForSendCoins.ts b/backend/src/federation/client/1_0/query/voteForSendCoins.ts index 0f16ff32b..9658cecfd 100644 --- a/backend/src/federation/client/1_0/query/voteForSendCoins.ts +++ b/backend/src/federation/client/1_0/query/voteForSendCoins.ts @@ -20,6 +20,10 @@ export const voteForSendCoins = gql` senderCommunityUuid: $senderCommunityUuid senderUserUuid: $senderUserUuid senderUserName: $senderUserName - ) + ) { + vote + recipGradidoID + recipName + } } ` diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index a59ca5c0a..20765e952 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -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( diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index fed605a54..baadd5c55 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -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 } diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 3bae8acd3..2512d69c6 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -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 { 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 diff --git a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts new file mode 100644 index 000000000..3eb1419b5 --- /dev/null +++ b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts @@ -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 +} diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 4196e015c..0f6f38633 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -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 { + ): Promise { 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) From 32496a035395ac0dfff83de244a9cd19f6d94479 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 20 Sep 2023 14:24:23 +0200 Subject: [PATCH 08/24] introduce tx-switch for local and x-com sendcoins --- .../graphql/resolver/TransactionResolver.ts | 33 ++++++++++++++----- .../resolver/util/processXComSendCoins.ts | 23 +++++++++---- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 00834830b..e7c2f751c 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -27,6 +27,7 @@ import { sendTransactionReceivedEmail, } from '@/emails/sendEmailVariants' import { EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Events' +import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' import { Context, getUser } from '@/server/context' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' @@ -41,10 +42,12 @@ import { isCommunityAuthenticated, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' -import { processXComCommittingSendCoins, processXComPendingSendCoins } from './util/processXComSendCoins' +import { + processXComCommittingSendCoins, + processXComPendingSendCoins, +} from './util/processXComSendCoins' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' import { transactionLinkSummary } from './util/transactionLinkSummary' -import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' export const executeTransaction = async ( amount: Decimal, @@ -537,7 +540,7 @@ export class TransactionResolver { where: { communityUuid: recipientCommunityIdentifier }, }) let pendingResult: SendCoinsResult - let commitingResult: SendCoinsResult + let committingResult: SendCoinsResult const creationDate = new Date() try { @@ -550,8 +553,8 @@ export class TransactionResolver { senderUser, recipientIdentifier, ) - if(pendingResult.vote && pendingResult.recipGradidoID) { - commitingResult = await processXComCommittingSendCoins( + if (pendingResult.vote && pendingResult.recipGradidoID) { + committingResult = await processXComCommittingSendCoins( recipCom, homeCom, creationDate, @@ -560,12 +563,26 @@ export class TransactionResolver { senderUser, pendingResult.recipGradidoID, ) - if(!commitingResult.vote) { - + if (!committingResult.vote) { + logger.fatal('FATAL ERROR: on processXComCommittingSendCoins for', committingResult) + throw new LogError( + 'FATAL ERROR: on processXComCommittingSendCoins with ', + recipientCommunityIdentifier, + recipientIdentifier, + amount.toString(), + memo, + ) } } } catch (err) { - + throw new LogError( + 'ERROR: on processXComCommittingSendCoins with ', + recipientCommunityIdentifier, + recipientIdentifier, + amount.toString(), + memo, + err, + ) } } return true diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 5809a862a..8b94e03c5 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -135,7 +135,7 @@ export async function processXComCommittingSendCoins( sender: dbUser, recipUuid: string, ): Promise { - let sendCoinsResult = new SendCoinsResult() + const sendCoinsResult = new SendCoinsResult() try { logger.debug( `XCom: processXComCommittingSendCoins...`, @@ -145,7 +145,7 @@ export async function processXComCommittingSendCoins( amount, memo, sender, - recipient, + recipUuid, ) // first find pending Tx with given parameters const pendingTx = await DbPendingTransaction.findOneBy({ @@ -188,12 +188,20 @@ export async function processXComCommittingSendCoins( args.senderUserName = pendingTx.userName } logger.debug(`X-Com: ready for settleSendCoins with args=`, args) - const acknoleged = await client.settleSendCoins(args) - logger.debug(`X-Com: returnd from settleSendCoins:`, acknoleged) - if (acknoleged) { + const acknowledge = await client.settleSendCoins(args) + logger.debug(`X-Com: returnd from settleSendCoins:`, acknowledge) + if (acknowledge) { // settle the pending transaction on receiver-side was successfull, so now settle the sender side try { - await settlePendingSenderTransaction(senderCom, sender, pendingTx) + sendCoinsResult.vote = await settlePendingSenderTransaction( + senderCom, + sender, + pendingTx, + ) + if (sendCoinsResult.vote) { + sendCoinsResult.recipName = pendingTx.linkedUserName + sendCoinsResult.recipGradidoID = pendingTx.linkedUserGradidoID + } } catch (err) { logger.error(`Error in writing sender pending transaction: `, err) // revert the existing pending transaction on receiver side @@ -219,6 +227,7 @@ export async function processXComCommittingSendCoins( } } catch (err) { logger.error(`Error:`, err) + sendCoinsResult.vote = false } - return true + return sendCoinsResult } From fe85e45d385dcbc33ac6d86ec07554bb06403fc7 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 20 Sep 2023 16:47:59 +0200 Subject: [PATCH 09/24] correct graphql result definition for voteForSendCoins --- .../src/federation/client/1_0/SendCoinsClient.ts | 11 +++++++---- .../graphql/resolver/util/processXComSendCoins.ts | 2 +- .../src/graphql/api/1_0/model/SendCoinsResult.ts | 8 ++++---- .../api/1_0/resolver/SendCoinsResolver.test.ts | 15 ++++++++++++--- .../graphql/api/1_0/resolver/SendCoinsResolver.ts | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index f9c318e81..c03755c16 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -34,18 +34,21 @@ export class SendCoinsClient { voteForSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug('X-Com: voteForSendCoins against endpoint=', this.endpoint) try { + logger.debug(`SendCoinsClient: voteForSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(voteForSendCoins, { args }) + logger.debug(`SendCoinsClient: after rawRequest...`, data) + const sendCoinsResult = data as SendCoinsResult // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.voteForSendCoins?.vote) { + if (!sendCoinsResult.vote) { logger.warn( 'X-Com: voteForSendCoins failed with: ', // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - data.voteForSendCoins.recipGradidoID, + sendCoinsResult.recipGradidoID, // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - data.voteForSendCoins.recipName, + sendCoinsResult.recipName, ) - return new SendCoinsResult() + return sendCoinsResult } const result = new SendCoinsResult() result.vote = true diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 8b94e03c5..5f1349b70 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -121,7 +121,7 @@ export async function processXComPendingSendCoins( } } } catch (err) { - logger.error(`Error:`, err) + throw new LogError(`Error:`, err) } return sendCoinsResult } diff --git a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts index 3eb1419b5..749ca87cb 100644 --- a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts +++ b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts @@ -1,6 +1,6 @@ -import { ArgsType, Field } from 'type-graphql' +import { Field, ObjectType } from 'type-graphql' -@ArgsType() +@ObjectType() export class SendCoinsResult { constructor() { this.vote = false @@ -9,9 +9,9 @@ export class SendCoinsResult { @Field(() => Boolean) vote: boolean - @Field(() => String, { nullable: true }) + @Field(() => String) recipGradidoID: string | null - @Field(() => String, { nullable: true }) + @Field(() => String) recipName: string | null } diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index 5120391d6..c84965ce2 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -68,8 +68,13 @@ describe('SendCoinsResolver', () => { senderCommunityUuid: $senderCommunityUuid senderUserUuid: $senderUserUuid senderUserName: $senderUserName - ) - }` + ) { + vote + recipGradidoID + recipName + } + } +` const settleSendCoinsMutation = ` mutation ( @@ -220,7 +225,11 @@ describe('SendCoinsResolver', () => { ).toEqual( expect.objectContaining({ data: { - voteForSendCoins: 'recipUser-FirstName recipUser-LastName', + voteForSendCoins: { + recipGradidoID: '56a55482-909e-46a4-bfa2-cd025e894ebd', + recipName: 'recipUser-FirstName recipUser-LastName', + vote: true, + }, }, }), ) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 0f6f38633..ccae52edc 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -19,7 +19,7 @@ import { SendCoinsResult } from '../model/SendCoinsResult' @Resolver() // eslint-disable-next-line @typescript-eslint/no-unused-vars export class SendCoinsResolver { - @Mutation(() => String) + @Mutation(() => SendCoinsResult) async voteForSendCoins( @Args() { From 2fe9b4e55e3598c819cdcf15da933485b60b8f98 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 20 Sep 2023 17:01:25 +0200 Subject: [PATCH 10/24] changed CONFIG.VERSION distributable in .env.template for deployment --- backend/.env.template | 3 ++- dht-node/.env.template | 3 ++- federation/.env.template | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/.env.template b/backend/.env.template index 06bf81088..e79122368 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -1,4 +1,5 @@ -CONFIG_VERSION=$BACKEND_CONFIG_VERSION +# must match the CONFIG_VERSION.EXPECTED definition in scr/config/index.ts +CONFIG_VERSION=v20.2023-09-19 # Server JWT_SECRET=$JWT_SECRET diff --git a/dht-node/.env.template b/dht-node/.env.template index a7603c15a..1278f61be 100644 --- a/dht-node/.env.template +++ b/dht-node/.env.template @@ -1,4 +1,5 @@ -CONFIG_VERSION=$FEDERATION_DHT_CONFIG_VERSION +# must match the CONFIG_VERSION.EXPECTED definition in scr/config/index.ts +CONFIG_VERSION=v3.2023-04-26 # Database DB_HOST=localhost diff --git a/federation/.env.template b/federation/.env.template index 295c3c480..e6ac8ad7d 100644 --- a/federation/.env.template +++ b/federation/.env.template @@ -1,4 +1,5 @@ -CONFIG_VERSION=$FEDERATION_CONFIG_VERSION +# must match the CONFIG_VERSION.EXPECTED definition in scr/config/index.ts +CONFIG_VERSION=v2.2023-08-24 LOG_LEVEL=$LOG_LEVEL # this is set fix to false, because it is important for 'production' environments. only set to true if a graphql-playground should be in use From d5ea0b7724fa153864693c4599a78638c6db7b69 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 20 Sep 2023 17:48:24 +0200 Subject: [PATCH 11/24] next try for graphql api definition --- .../federation/client/1_0/SendCoinsClient.ts | 2 +- .../api/1_0/resolver/SendCoinsResolver.ts | 178 ++++++++++-------- 2 files changed, 103 insertions(+), 77 deletions(-) diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index c03755c16..359393cda 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -23,7 +23,7 @@ export class SendCoinsClient { dbCom.apiVersion }/` this.client = new GraphQLClient(this.endpoint, { - method: 'GET', + method: 'POST', jsonSerializer: { parse: JSON.parse, stringify: JSON.stringify, diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index ccae52edc..054bdfdd3 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -1,5 +1,5 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { Args, Mutation, Resolver } from 'type-graphql' +import { Arg, Args, Mutation, Resolver } from 'type-graphql' import { federationLogger as logger } from '@/server/logger' import { Community as DbCommunity } from '@entity/Community' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' @@ -21,7 +21,9 @@ import { SendCoinsResult } from '../model/SendCoinsResult' export class SendCoinsResolver { @Mutation(() => SendCoinsResult) async voteForSendCoins( - @Args() + @Arg('data') + args: SendCoinsArgs, + /* { recipientCommunityUuid, recipientUserIdentifier, @@ -31,34 +33,35 @@ export class SendCoinsResolver { senderCommunityUuid, senderUserUuid, senderUserName, - }: SendCoinsArgs, + } + */ ): Promise { logger.debug( `voteForSendCoins() via apiVersion=1_0 ...`, - recipientCommunityUuid, - recipientUserIdentifier, - creationDate, - amount.toString(), - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, + args.recipientCommunityUuid, + args.recipientUserIdentifier, + args.creationDate, + args.amount.toString(), + args.memo, + args.senderCommunityUuid, + args.senderUserUuid, + args.senderUserName, ) const result = new SendCoinsResult() // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: recipientCommunityUuid, + communityUuid: args.recipientCommunityUuid, }) if (!homeCom) { throw new LogError( `voteForSendCoins with wrong recipientCommunityUuid`, - recipientCommunityUuid, + args.recipientCommunityUuid, ) } let receiverUser try { // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(recipientUserIdentifier) + receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) } catch (err) { logger.error('Error in findUserByIdentifier:', err) throw new LogError( @@ -67,24 +70,24 @@ export class SendCoinsResolver { ) } try { - const txDate = new Date(creationDate) - const receiveBalance = await calculateRecipientBalance(receiverUser.id, amount, txDate) + const txDate = new Date(args.creationDate) + const receiveBalance = await calculateRecipientBalance(receiverUser.id, args.amount, txDate) const pendingTx = DbPendingTransaction.create() - pendingTx.amount = amount - pendingTx.balance = receiveBalance ? receiveBalance.balance : amount + pendingTx.amount = args.amount + pendingTx.balance = receiveBalance ? receiveBalance.balance : args.amount pendingTx.balanceDate = txDate pendingTx.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) pendingTx.decayStart = receiveBalance ? receiveBalance.decay.start : null pendingTx.creationDate = new Date() - pendingTx.linkedUserCommunityUuid = senderCommunityUuid - pendingTx.linkedUserGradidoID = senderUserUuid - pendingTx.linkedUserName = senderUserName - pendingTx.memo = memo + pendingTx.linkedUserCommunityUuid = args.senderCommunityUuid + pendingTx.linkedUserGradidoID = args.senderUserUuid + pendingTx.linkedUserName = args.senderUserName + pendingTx.memo = args.memo pendingTx.previous = receiveBalance ? receiveBalance.lastTransactionId : null pendingTx.state = PendingTransactionState.NEW pendingTx.typeId = TransactionTypeId.RECEIVE pendingTx.userId = receiverUser.id - pendingTx.userCommunityUuid = recipientCommunityUuid + pendingTx.userCommunityUuid = args.recipientCommunityUuid pendingTx.userGradidoID = receiverUser.gradidoID pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) @@ -101,7 +104,9 @@ export class SendCoinsResolver { @Mutation(() => Boolean) async revertSendCoins( - @Args() + @Arg('data') + args: SendCoinsArgs, + /* { recipientCommunityUuid, recipientUserIdentifier, @@ -112,22 +117,23 @@ export class SendCoinsResolver { senderUserUuid, senderUserName, }: SendCoinsArgs, + */ ): Promise { logger.debug(`revertSendCoins() via apiVersion=1_0 ...`) // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: recipientCommunityUuid, + communityUuid: args.recipientCommunityUuid, }) if (!homeCom) { throw new LogError( `revertSendCoins with wrong recipientCommunityUuid`, - recipientCommunityUuid, + args.recipientCommunityUuid, ) } let receiverUser try { // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(recipientUserIdentifier) + receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) } catch (err) { logger.error('Error in findUserByIdentifier:', err) throw new LogError( @@ -137,16 +143,16 @@ export class SendCoinsResolver { } try { const pendingTx = await DbPendingTransaction.findOneBy({ - userCommunityUuid: recipientCommunityUuid, + userCommunityUuid: args.recipientCommunityUuid, userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW, typeId: TransactionTypeId.RECEIVE, - balanceDate: new Date(creationDate), - linkedUserCommunityUuid: senderCommunityUuid, - linkedUserGradidoID: senderUserUuid, + balanceDate: new Date(args.creationDate), + linkedUserCommunityUuid: args.senderCommunityUuid, + linkedUserGradidoID: args.senderUserUuid, }) logger.debug('XCom: revertSendCoins found pendingTX=', pendingTx) - if (pendingTx && pendingTx.amount.toString() === amount.toString()) { + if (pendingTx && pendingTx.amount.toString() === args.amount.toString()) { logger.debug('XCom: revertSendCoins matching pendingTX for remove...') try { await pendingTx.remove() @@ -158,20 +164,20 @@ export class SendCoinsResolver { logger.debug( 'XCom: revertSendCoins NOT matching pendingTX for remove:', pendingTx?.amount, - amount, + args.amount, ) throw new LogError( `Can't find in revertSendCoins the pending receiver TX for args=`, - recipientCommunityUuid, - recipientUserIdentifier, + args.recipientCommunityUuid, + args.recipientUserIdentifier, PendingTransactionState.NEW, TransactionTypeId.RECEIVE, - creationDate, - amount, - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, + args.creationDate, + args.amount, + args.memo, + args.senderCommunityUuid, + args.senderUserUuid, + args.senderUserName, ) } logger.debug(`revertSendCoins()-1_0... successfull`) @@ -183,7 +189,9 @@ export class SendCoinsResolver { @Mutation(() => Boolean) async settleSendCoins( - @Args() + @Arg('data') + args: SendCoinsArgs, + /* { recipientCommunityUuid, recipientUserIdentifier, @@ -194,24 +202,31 @@ export class SendCoinsResolver { senderUserUuid, senderUserName, }: SendCoinsArgs, + */ ): Promise { logger.debug( - `settleSendCoins() via apiVersion=1_0 ...userCommunityUuid=${recipientCommunityUuid}, userGradidoID=${recipientUserIdentifier}, balanceDate=${creationDate},amount=${amount.valueOf()}, memo=${memo}, linkedUserCommunityUuid = ${senderCommunityUuid}, userSenderIdentifier=${senderUserUuid}, userSenderName=${senderUserName}`, + `settleSendCoins() via apiVersion=1_0 ...userCommunityUuid=${ + args.recipientCommunityUuid + }, userGradidoID=${args.recipientUserIdentifier}, balanceDate=${ + args.creationDate + },amount=${args.amount.valueOf()}, memo=${args.memo}, linkedUserCommunityUuid = ${ + args.senderCommunityUuid + }, userSenderIdentifier=${args.senderUserUuid}, userSenderName=${args.senderUserName}`, ) // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: recipientCommunityUuid, + communityUuid: args.recipientCommunityUuid, }) if (!homeCom) { throw new LogError( `settleSendCoins with wrong recipientCommunityUuid`, - recipientCommunityUuid, + args.recipientCommunityUuid, ) } let receiverUser try { // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(recipientUserIdentifier) + receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) } catch (err) { logger.error('Error in findUserByIdentifier:', err) throw new LogError( @@ -220,16 +235,20 @@ export class SendCoinsResolver { ) } const pendingTx = await DbPendingTransaction.findOneBy({ - userCommunityUuid: recipientCommunityUuid, + userCommunityUuid: args.recipientCommunityUuid, userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW, typeId: TransactionTypeId.RECEIVE, - balanceDate: new Date(creationDate), - linkedUserCommunityUuid: senderCommunityUuid, - linkedUserGradidoID: senderUserUuid, + balanceDate: new Date(args.creationDate), + linkedUserCommunityUuid: args.senderCommunityUuid, + linkedUserGradidoID: args.senderUserUuid, }) logger.debug('XCom: settleSendCoins found pendingTX=', pendingTx?.toString()) - if (pendingTx && pendingTx.amount.toString() === amount.toString() && pendingTx.memo === memo) { + if ( + pendingTx && + pendingTx.amount.toString() === args.amount.toString() && + pendingTx.memo === args.memo + ) { logger.debug('XCom: settleSendCoins matching pendingTX for settlement...') await settlePendingReceiveTransaction(homeCom, receiverUser, pendingTx) @@ -239,23 +258,25 @@ export class SendCoinsResolver { logger.debug('XCom: settlePendingReceiveTransaction NOT matching pendingTX for settlement...') throw new LogError( `Can't find in settlePendingReceiveTransaction the pending receiver TX for args=`, - recipientCommunityUuid, - recipientUserIdentifier, + args.recipientCommunityUuid, + args.recipientUserIdentifier, PendingTransactionState.NEW, TransactionTypeId.RECEIVE, - creationDate, - amount, - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, + args.creationDate, + args.amount, + args.memo, + args.senderCommunityUuid, + args.senderUserUuid, + args.senderUserName, ) } } @Mutation(() => Boolean) async revertSettledSendCoins( - @Args() + @Arg('data') + args: SendCoinsArgs, + /* { recipientCommunityUuid, recipientUserIdentifier, @@ -266,22 +287,23 @@ export class SendCoinsResolver { senderUserUuid, senderUserName, }: SendCoinsArgs, + */ ): Promise { logger.debug(`revertSettledSendCoins() via apiVersion=1_0 ...`) // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: recipientCommunityUuid, + communityUuid: args.recipientCommunityUuid, }) if (!homeCom) { throw new LogError( `revertSettledSendCoins with wrong recipientCommunityUuid`, - recipientCommunityUuid, + args.recipientCommunityUuid, ) } let receiverUser try { // second check if receiver user exists in this community - receiverUser = await findUserByIdentifier(recipientUserIdentifier) + receiverUser = await findUserByIdentifier(args.recipientUserIdentifier) } catch (err) { logger.error('Error in findUserByIdentifier:', err) throw new LogError( @@ -290,16 +312,20 @@ export class SendCoinsResolver { ) } const pendingTx = await DbPendingTransaction.findOneBy({ - userCommunityUuid: recipientCommunityUuid, + userCommunityUuid: args.recipientCommunityUuid, userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.SETTLED, typeId: TransactionTypeId.RECEIVE, - balanceDate: new Date(creationDate), - linkedUserCommunityUuid: senderCommunityUuid, - linkedUserGradidoID: senderUserUuid, + balanceDate: new Date(args.creationDate), + linkedUserCommunityUuid: args.senderCommunityUuid, + linkedUserGradidoID: args.senderUserUuid, }) logger.debug('XCom: revertSettledSendCoins found pendingTX=', pendingTx) - if (pendingTx && pendingTx.amount.toString() === amount.toString() && pendingTx.memo === memo) { + if ( + pendingTx && + pendingTx.amount.toString() === args.amount.toString() && + pendingTx.memo === args.memo + ) { logger.debug('XCom: revertSettledSendCoins matching pendingTX for remove...') try { await revertSettledReceiveTransaction(homeCom, receiverUser, pendingTx) @@ -311,16 +337,16 @@ export class SendCoinsResolver { logger.debug('XCom: revertSettledSendCoins NOT matching pendingTX...') throw new LogError( `Can't find in revertSettledSendCoins the pending receiver TX for args=`, - recipientCommunityUuid, - recipientUserIdentifier, + args.recipientCommunityUuid, + args.recipientUserIdentifier, PendingTransactionState.SETTLED, TransactionTypeId.RECEIVE, - creationDate, - amount, - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, + args.creationDate, + args.amount, + args.memo, + args.senderCommunityUuid, + args.senderUserUuid, + args.senderUserName, ) } logger.debug(`revertSendCoins()-1_0... successfull`) From 34be808472dc16e0439b0ad0e7c11760845b33df Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 21 Sep 2023 00:10:34 +0200 Subject: [PATCH 12/24] change SendCoinsArgs to @InputType() --- federation/src/graphql/api/1_0/model/SendCoinsArgs.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts index fb97da925..e658209ca 100644 --- a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts +++ b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts @@ -1,7 +1,7 @@ import { Decimal } from 'decimal.js-light' -import { ArgsType, Field } from 'type-graphql' +import { Field, InputType } from 'type-graphql' -@ArgsType() +@InputType() export class SendCoinsArgs { @Field(() => String) recipientCommunityUuid: string From 7561025c99589d9c7fafd40ff32a3fa2e0928e42 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 21 Sep 2023 16:11:00 +0200 Subject: [PATCH 13/24] next try of graphql mutation --- .../client/1_0/model/SendCoinsResult.ts | 4 +-- .../client/1_0/query/voteForSendCoins.ts | 30 +++++++++++++------ .../api/1_0/resolver/SendCoinsResolver.ts | 12 -------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/backend/src/federation/client/1_0/model/SendCoinsResult.ts b/backend/src/federation/client/1_0/model/SendCoinsResult.ts index d3ce76392..1258db4cb 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsResult.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsResult.ts @@ -1,6 +1,6 @@ -import { ArgsType, Field } from 'type-graphql' +import { Field, ObjectType } from 'type-graphql' -@ArgsType() +@ObjectType() export class SendCoinsResult { constructor() { this.vote = false diff --git a/backend/src/federation/client/1_0/query/voteForSendCoins.ts b/backend/src/federation/client/1_0/query/voteForSendCoins.ts index 9658cecfd..a5456bec7 100644 --- a/backend/src/federation/client/1_0/query/voteForSendCoins.ts +++ b/backend/src/federation/client/1_0/query/voteForSendCoins.ts @@ -1,6 +1,16 @@ import { gql } from 'graphql-request' export const voteForSendCoins = gql` + mutation ($args: SendCoinsArgs!) { + voteForSendCoins(data: $args) { + vote + recipGradidoID + recipName + } + } +` + +/* mutation ( $recipientCommunityUuid: String! $recipientUserIdentifier: String! @@ -12,18 +22,20 @@ export const voteForSendCoins = gql` $senderUserName: String! ) { voteForSendCoins( - recipientCommunityUuid: $recipientCommunityUuid - recipientUserIdentifier: $recipientUserIdentifier - creationDate: $creationDate - amount: $amount - memo: $memo - senderCommunityUuid: $senderCommunityUuid - senderUserUuid: $senderUserUuid - senderUserName: $senderUserName + data: { + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier + creationDate: $creationDate + amount: $amount + memo: $memo + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + senderUserName: $senderUserName + } ) { vote recipGradidoID recipName } } -` +*/ diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 054bdfdd3..4a31b0bbb 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -23,18 +23,6 @@ export class SendCoinsResolver { async voteForSendCoins( @Arg('data') args: SendCoinsArgs, - /* - { - recipientCommunityUuid, - recipientUserIdentifier, - creationDate, - amount, - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, - } - */ ): Promise { logger.debug( `voteForSendCoins() via apiVersion=1_0 ...`, From 31d47d753743c52f4d362204f9d65dd99fb29e1c Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 21 Sep 2023 18:45:53 +0200 Subject: [PATCH 14/24] next try for x-com sendCoins --- backend/log4js-config.json | 1 + .../federation/client/1_0/SendCoinsClient.ts | 57 ++++++++++++------- .../client/1_0/model/SendCoinsResult.ts | 4 +- .../client/1_0/query/revertSendCoins.ts | 7 ++- .../1_0/query/revertSettledSendCoins.ts | 8 ++- .../client/1_0/query/settleSendCoins.ts | 7 ++- .../graphql/resolver/TransactionResolver.ts | 6 +- .../src/graphql/resolver/util/communities.ts | 34 ++++++----- .../resolver/util/processXComSendCoins.ts | 25 +++++--- .../graphql/api/1_0/model/SendCoinsResult.ts | 4 +- .../api/1_0/resolver/SendCoinsResolver.ts | 54 ++++++------------ 11 files changed, 119 insertions(+), 88 deletions(-) diff --git a/backend/log4js-config.json b/backend/log4js-config.json index 1cbd4519c..90111a5ba 100644 --- a/backend/log4js-config.json +++ b/backend/log4js-config.json @@ -121,6 +121,7 @@ { "appenders": [ + "out", "backend", "errors" ], diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 359393cda..e8bd5c578 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -34,21 +34,14 @@ export class SendCoinsClient { voteForSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug('X-Com: voteForSendCoins against endpoint=', this.endpoint) try { - logger.debug(`SendCoinsClient: voteForSendCoins with args=`, args) + logger.debug(`X-Com: SendCoinsClient: voteForSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(voteForSendCoins, { args }) - logger.debug(`SendCoinsClient: after rawRequest...`, data) - const sendCoinsResult = data as SendCoinsResult + logger.debug(`X-Com: SendCoinsClient: after rawRequest...data:`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!sendCoinsResult.vote) { - logger.warn( - 'X-Com: voteForSendCoins failed with: ', - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - sendCoinsResult.recipGradidoID, - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - sendCoinsResult.recipName, - ) - return sendCoinsResult + if (!data?.voteForSendCoins?.vote) { + logger.warn('X-Com: voteForSendCoins failed with: ', data) + return new SendCoinsResult() } const result = new SendCoinsResult() result.vote = true @@ -66,17 +59,24 @@ export class SendCoinsClient { revertSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug('X-Com: revertSendCoins against endpoint=', this.endpoint) try { + logger.debug(`X-Com: SendCoinsClient: revertSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(revertSendCoins, { args }) + logger.debug(`X-Com: SendCoinsClient: after revertSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!data?.revertSendCoins?.revertSendCoins) { logger.warn('X-Com: revertSendCoins without response data from endpoint', this.endpoint) return false } - logger.debug(`X-Com: revertSendCoins successful from endpoint=${this.endpoint}`) + logger.debug( + `X-Com: SendCoinsClient: revertSendCoins successful from endpoint=${this.endpoint}`, + ) return true } catch (err) { - logger.error(`X-Com: revertSendCoins failed for endpoint=${this.endpoint}`, err) + logger.error( + `X-Com: SendCoinsClient: revertSendCoins failed for endpoint=${this.endpoint}`, + err, + ) return false } } @@ -84,37 +84,54 @@ export class SendCoinsClient { settleSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug(`X-Com: settleSendCoins against endpoint='${this.endpoint}'...`) try { + logger.debug(`X-Com: SendCoinsClient: settleSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(settleSendCoins, { args }) + logger.debug(`X-Com: SendCoinsClient: after settleSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!data?.settleSendCoins?.acknowledged) { - logger.warn('X-Com: settleSendCoins without response data from endpoint', this.endpoint) + logger.warn( + 'X-Com: SendCoinsClient: settleSendCoins without response data from endpoint', + this.endpoint, + ) return false } - logger.debug(`X-Com: settleSendCoins successful from endpoint=${this.endpoint}`) + logger.debug( + `X-Com: SendCoinsClient: settleSendCoins successful from endpoint=${this.endpoint}`, + ) return true } catch (err) { - throw new LogError(`X-Com: settleSendCoins failed for endpoint=${this.endpoint}`, err) + throw new LogError( + `X-Com: SendCoinsClient: settleSendCoins failed for endpoint=${this.endpoint}`, + err, + ) } } revertSettledSendCoins = async (args: SendCoinsArgs): Promise => { logger.debug(`X-Com: revertSettledSendCoins against endpoint='${this.endpoint}'...`) try { + logger.debug(`X-Com: SendCoinsClient: revertSettledSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { data } = await this.client.rawRequest(revertSettledSendCoins, { args }) + logger.debug(`X-Com: SendCoinsClient: after revertSettledSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!data?.revertSettledSendCoins?.acknowledged) { logger.warn( - 'X-Com: revertSettledSendCoins without response data from endpoint', + 'X-Com: SendCoinsClient: revertSettledSendCoins without response data from endpoint', this.endpoint, ) return false } - logger.debug(`X-Com: revertSettledSendCoins successful from endpoint=${this.endpoint}`) + logger.debug( + `X-Com: SendCoinsClient: revertSettledSendCoins successful from endpoint=${this.endpoint}`, + ) return true } catch (err) { - throw new LogError(`X-Com: revertSettledSendCoins failed for endpoint=${this.endpoint}`, err) + throw new LogError( + `X-Com: SendCoinsClient: revertSettledSendCoins failed for endpoint=${this.endpoint}`, + err, + ) } } } diff --git a/backend/src/federation/client/1_0/model/SendCoinsResult.ts b/backend/src/federation/client/1_0/model/SendCoinsResult.ts index 1258db4cb..930d22ff5 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsResult.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsResult.ts @@ -10,8 +10,8 @@ export class SendCoinsResult { vote: boolean @Field(() => String, { nullable: true }) - recipGradidoID: string | null | undefined + recipGradidoID: string | null @Field(() => String, { nullable: true }) - recipName: string | null | undefined + recipName: string | null } diff --git a/backend/src/federation/client/1_0/query/revertSendCoins.ts b/backend/src/federation/client/1_0/query/revertSendCoins.ts index 9cc23fe64..ea7d28f77 100644 --- a/backend/src/federation/client/1_0/query/revertSendCoins.ts +++ b/backend/src/federation/client/1_0/query/revertSendCoins.ts @@ -1,6 +1,11 @@ import { gql } from 'graphql-request' export const revertSendCoins = gql` + mutation ($args: SendCoinsArgs!) { + revertSendCoins(data: $args) + } +` +/* mutation ( $recipientCommunityUuid: String! $recipientUserIdentifier: String! @@ -22,4 +27,4 @@ export const revertSendCoins = gql` senderUserName: $senderUserName ) } -` +*/ diff --git a/backend/src/federation/client/1_0/query/revertSettledSendCoins.ts b/backend/src/federation/client/1_0/query/revertSettledSendCoins.ts index 74cdbd867..3965df396 100644 --- a/backend/src/federation/client/1_0/query/revertSettledSendCoins.ts +++ b/backend/src/federation/client/1_0/query/revertSettledSendCoins.ts @@ -1,6 +1,12 @@ import { gql } from 'graphql-request' export const revertSettledSendCoins = gql` + mutation ($args: SendCoinsArgs!) { + revertSettledSendCoins(data: $args) + } +` +/* + mutation ( $recipientCommunityUuid: String! $recipientUserIdentifier: String! @@ -22,4 +28,4 @@ export const revertSettledSendCoins = gql` senderUserName: $senderUserName ) } -` +*/ diff --git a/backend/src/federation/client/1_0/query/settleSendCoins.ts b/backend/src/federation/client/1_0/query/settleSendCoins.ts index 1696a0900..206a6544c 100644 --- a/backend/src/federation/client/1_0/query/settleSendCoins.ts +++ b/backend/src/federation/client/1_0/query/settleSendCoins.ts @@ -1,6 +1,11 @@ import { gql } from 'graphql-request' export const settleSendCoins = gql` + mutation ($args: SendCoinsArgs!) { + settleSendCoins(data: $args) + } +` +/* mutation ( $recipientCommunityUuid: String! $recipientUserIdentifier: String! @@ -22,4 +27,4 @@ export const settleSendCoins = gql` senderUserName: $senderUserName ) } -` +*/ \ No newline at end of file diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index e7c2f751c..0c35ed401 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -74,7 +74,9 @@ export const executeTransaction = async ( ], }) if (openSenderPendingTx > 0 || openReceiverPendingTx > 0) { - throw new LogError('There are still pending Transactions for Sender and/or Recipient') + throw new LogError( + `There exist still ongoing 'Pending-Transactions' for the involved users on sender-side!`, + ) } if (sender.id === recipient.id) { @@ -529,7 +531,7 @@ export class TransactionResolver { logger.info('successful executeTransaction', amount, memo, senderUser, recipientUser) } else { // processing a x-community sendCoins - logger.debug('processing a x-community transaction...') + logger.debug('X-Com: processing a x-community transaction...') if (!CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED) { throw new LogError('X-Community sendCoins disabled per configuration!') } diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index 59e79f077..f55816229 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -1,14 +1,14 @@ import { Community as DbCommunity } from '@entity/Community' export async function isHomeCommunity(communityIdentifier: string): Promise { - const homeCommunity = await DbCommunity.findOneByOrFail({ foreign: false }) - if (communityIdentifier === homeCommunity.id.toString()) { - return true - } else if (communityIdentifier === homeCommunity.name) { - return true - } else if (communityIdentifier === homeCommunity.communityUuid) { - return true - } else if (communityIdentifier === homeCommunity.url) { + const homeCommunity = await DbCommunity.findOne({ + where: [ + { foreign: false, communityUuid: communityIdentifier }, + { foreign: false, name: communityIdentifier }, + { foreign: false, url: communityIdentifier }, + ], + }) + if (homeCommunity) { return true } else { return false @@ -16,11 +16,6 @@ export async function isHomeCommunity(communityIdentifier: string): Promise { - const community = await DbCommunity.findOneByOrFail({ name: communityIdentifier }) - return community.url -} - -export async function isCommunityAuthenticated(communityIdentifier: string): Promise { const community = await DbCommunity.findOneOrFail({ where: [ { communityUuid: communityIdentifier }, @@ -28,7 +23,18 @@ export async function isCommunityAuthenticated(communityIdentifier: string): Pro { url: communityIdentifier }, ], }) - if (community.authenticatedAt) { + return community.url +} + +export async function isCommunityAuthenticated(communityIdentifier: string): Promise { + const community = await DbCommunity.findOne({ + where: [ + { communityUuid: communityIdentifier }, + { name: communityIdentifier }, + { url: communityIdentifier }, + ], + }) + if (community?.authenticatedAt) { return true } else { return false diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 5f1349b70..1cd443cd4 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -28,7 +28,7 @@ export async function processXComPendingSendCoins( sender: dbUser, recipientIdentifier: string, ): Promise { - let sendCoinsResult = new SendCoinsResult() + let voteResult: SendCoinsResult try { logger.debug( `XCom: processXComPendingSendCoins...`, @@ -69,9 +69,10 @@ export async function processXComPendingSendCoins( args.senderUserUuid = sender.gradidoID args.senderUserName = fullName(sender.firstName, sender.lastName) logger.debug(`X-Com: ready for voteForSendCoins with args=`, args) - sendCoinsResult = await client.voteForSendCoins(args) - logger.debug(`X-Com: returnd from voteForSendCoins:`, sendCoinsResult) - if (sendCoinsResult.vote) { + voteResult = await client.voteForSendCoins(args) + logger.debug(`X-Com: returnd from voteForSendCoins:`, voteResult) + if (voteResult.vote) { + logger.debug(`X-Com: prepare pendingTransaction for sender...`) // writing the pending transaction on receiver-side was successfull, so now write the sender side try { const pendingTx = DbPendingTransaction.create() @@ -83,17 +84,18 @@ export async function processXComPendingSendCoins( if (receiverCom.communityUuid) { pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid } - if (sendCoinsResult.recipGradidoID) { - pendingTx.linkedUserGradidoID = sendCoinsResult.recipGradidoID + if (voteResult.recipGradidoID) { + pendingTx.linkedUserGradidoID = voteResult.recipGradidoID } - if (sendCoinsResult.recipName) { - pendingTx.linkedUserName = sendCoinsResult.recipName + if (voteResult.recipName) { + pendingTx.linkedUserName = voteResult.recipName } pendingTx.memo = memo pendingTx.previous = senderBalance ? senderBalance.lastTransactionId : null pendingTx.state = PendingTransactionState.NEW pendingTx.typeId = TransactionTypeId.SEND if (senderCom.communityUuid) pendingTx.userCommunityUuid = senderCom.communityUuid + pendingTx.id = sender.id pendingTx.userGradidoID = sender.gradidoID pendingTx.userName = fullName(sender.firstName, sender.lastName) logger.debug(`X-Com: initialized sender pendingTX=`, pendingTx) @@ -118,12 +120,17 @@ export async function processXComPendingSendCoins( ) } logger.debug(`voteForSendCoins()-1_0... successfull`) + } else { + logger.debug( + `X-Com: break with error on writing pendingTransaction for recipient...`, + voteResult, + ) } } } catch (err) { throw new LogError(`Error:`, err) } - return sendCoinsResult + return new SendCoinsResult() } export async function processXComCommittingSendCoins( diff --git a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts index 749ca87cb..930d22ff5 100644 --- a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts +++ b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts @@ -9,9 +9,9 @@ export class SendCoinsResult { @Field(() => Boolean) vote: boolean - @Field(() => String) + @Field(() => String, { nullable: true }) recipGradidoID: string | null - @Field(() => String) + @Field(() => String, { nullable: true }) recipName: string | null } diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 4a31b0bbb..caa60c0d1 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -57,6 +57,24 @@ export class SendCoinsResolver { homeCom.name, ) } + const openSenderPendingTx = await DbPendingTransaction.count({ + where: [ + { userGradidoID: args.senderUserUuid, state: PendingTransactionState.NEW }, + { linkedUserGradidoID: args.senderUserUuid, state: PendingTransactionState.NEW }, + ], + }) + const openReceiverPendingTx = await DbPendingTransaction.count({ + where: [ + { userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW }, + { linkedUserGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW }, + ], + }) + if (openSenderPendingTx > 0 || openReceiverPendingTx > 0) { + throw new LogError( + `There exist still ongoing 'Pending-Transactions' for the involved users on receiver-side!`, + ) + } + try { const txDate = new Date(args.creationDate) const receiveBalance = await calculateRecipientBalance(receiverUser.id, args.amount, txDate) @@ -94,18 +112,6 @@ export class SendCoinsResolver { async revertSendCoins( @Arg('data') args: SendCoinsArgs, - /* - { - recipientCommunityUuid, - recipientUserIdentifier, - creationDate, - amount, - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, - }: SendCoinsArgs, - */ ): Promise { logger.debug(`revertSendCoins() via apiVersion=1_0 ...`) // first check if receiver community is correct @@ -179,18 +185,6 @@ export class SendCoinsResolver { async settleSendCoins( @Arg('data') args: SendCoinsArgs, - /* - { - recipientCommunityUuid, - recipientUserIdentifier, - creationDate, - amount, - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, - }: SendCoinsArgs, - */ ): Promise { logger.debug( `settleSendCoins() via apiVersion=1_0 ...userCommunityUuid=${ @@ -264,18 +258,6 @@ export class SendCoinsResolver { async revertSettledSendCoins( @Arg('data') args: SendCoinsArgs, - /* - { - recipientCommunityUuid, - recipientUserIdentifier, - creationDate, - amount, - memo, - senderCommunityUuid, - senderUserUuid, - senderUserName, - }: SendCoinsArgs, - */ ): Promise { logger.debug(`revertSettledSendCoins() via apiVersion=1_0 ...`) // first check if receiver community is correct From dfa2a223db455d8f94ad6bd4acf9211d692b9e00 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 22 Sep 2023 00:30:30 +0200 Subject: [PATCH 15/24] next try x-com-sendCoins --- .../federation/client/1_0/SendCoinsClient.ts | 6 +- .../graphql/resolver/TransactionResolver.ts | 152 ------------------ .../resolver/util/processXComSendCoins.ts | 35 ++-- .../util/settlePendingSenderTransaction.ts | 13 +- .../api/1_0/resolver/SendCoinsResolver.ts | 9 +- .../util/settlePendingReceiveTransaction.ts | 15 +- 6 files changed, 46 insertions(+), 184 deletions(-) diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index e8bd5c578..3b3bf4f2c 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -64,7 +64,7 @@ export class SendCoinsClient { const { data } = await this.client.rawRequest(revertSendCoins, { args }) logger.debug(`X-Com: SendCoinsClient: after revertSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.revertSendCoins?.revertSendCoins) { + if (!data?.revertSendCoins) { logger.warn('X-Com: revertSendCoins without response data from endpoint', this.endpoint) return false } @@ -89,7 +89,7 @@ export class SendCoinsClient { const { data } = await this.client.rawRequest(settleSendCoins, { args }) logger.debug(`X-Com: SendCoinsClient: after settleSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.settleSendCoins?.acknowledged) { + if (!data?.settleSendCoins) { logger.warn( 'X-Com: SendCoinsClient: settleSendCoins without response data from endpoint', this.endpoint, @@ -116,7 +116,7 @@ export class SendCoinsClient { const { data } = await this.client.rawRequest(revertSettledSendCoins, { args }) logger.debug(`X-Com: SendCoinsClient: after revertSettledSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (!data?.revertSettledSendCoins?.acknowledged) { + if (!data?.revertSettledSendCoins) { logger.warn( 'X-Com: SendCoinsClient: revertSettledSendCoins without response data from endpoint', this.endpoint, diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 0c35ed401..73f998b43 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -209,158 +209,6 @@ export const executeTransaction = async ( return true } -/* -export const executeCrossTransaction = async ( - amount: Decimal, - memo: string, - sender: dbUser, - recipientIdentifier: string, - transactionLink?: dbTransactionLink | null, -): Promise => { - // acquire lock - const releaseLock = await TRANSACTIONS_LOCK.acquire() - try { - logger.info('executeCrossTransaction', amount, memo, sender, recipientIdentifier) - - if (sender.id === recipient.id) { - throw new LogError('Sender and Recipient are the same', sender.id) - } - - if (memo.length < MEMO_MIN_CHARS) { - throw new LogError('Memo text is too short', memo.length) - } - - if (memo.length > MEMO_MAX_CHARS) { - throw new LogError('Memo text is too long', memo.length) - } - - // validate amount - const receivedCallDate = new Date() - const sendBalance = await calculateBalance( - sender.id, - amount.mul(-1), - receivedCallDate, - transactionLink, - ) - logger.debug(`calculated Balance=${sendBalance}`) - if (!sendBalance) { - throw new LogError('User has not enough GDD or amount is < 0', sendBalance) - } - - const queryRunner = getConnection().createQueryRunner() - await queryRunner.connect() - await queryRunner.startTransaction('REPEATABLE READ') - logger.debug(`open Transaction to write...`) - try { - // transaction - const transactionSend = new dbTransaction() - transactionSend.typeId = TransactionTypeId.SEND - transactionSend.memo = memo - transactionSend.userId = sender.id - transactionSend.userGradidoID = sender.gradidoID - transactionSend.userName = fullName(sender.firstName, sender.lastName) - transactionSend.linkedUserId = recipient.id - transactionSend.linkedUserGradidoID = recipient.gradidoID - transactionSend.linkedUserName = fullName(recipient.firstName, recipient.lastName) - transactionSend.amount = amount.mul(-1) - transactionSend.balance = sendBalance.balance - transactionSend.balanceDate = receivedCallDate - transactionSend.decay = sendBalance.decay.decay - transactionSend.decayStart = sendBalance.decay.start - transactionSend.previous = sendBalance.lastTransactionId - transactionSend.transactionLinkId = transactionLink ? transactionLink.id : null - await queryRunner.manager.insert(dbTransaction, transactionSend) - - logger.debug(`sendTransaction inserted: ${dbTransaction}`) - - const transactionReceive = new dbTransaction() - transactionReceive.typeId = TransactionTypeId.RECEIVE - transactionReceive.memo = memo - transactionReceive.userId = recipient.id - transactionReceive.userGradidoID = recipient.gradidoID - transactionReceive.userName = fullName(recipient.firstName, recipient.lastName) - transactionReceive.linkedUserId = sender.id - transactionReceive.linkedUserGradidoID = sender.gradidoID - transactionReceive.linkedUserName = fullName(sender.firstName, sender.lastName) - transactionReceive.amount = amount - const receiveBalance = await calculateBalance(recipient.id, amount, receivedCallDate) - transactionReceive.balance = receiveBalance ? receiveBalance.balance : amount - transactionReceive.balanceDate = receivedCallDate - transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) - transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null - transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null - transactionReceive.linkedTransactionId = transactionSend.id - transactionReceive.transactionLinkId = transactionLink ? transactionLink.id : null - await queryRunner.manager.insert(dbTransaction, transactionReceive) - logger.debug(`receive Transaction inserted: ${dbTransaction}`) - - // Save linked transaction id for send - transactionSend.linkedTransactionId = transactionReceive.id - await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend) - logger.debug('send Transaction updated', transactionSend) - - if (transactionLink) { - logger.info('transactionLink', transactionLink) - transactionLink.redeemedAt = receivedCallDate - transactionLink.redeemedBy = recipient.id - await queryRunner.manager.update( - dbTransactionLink, - { id: transactionLink.id }, - transactionLink, - ) - } - - await queryRunner.commitTransaction() - logger.info(`commit Transaction successful...`) - - await EVENT_TRANSACTION_SEND(sender, recipient, transactionSend, transactionSend.amount) - - await EVENT_TRANSACTION_RECEIVE( - recipient, - sender, - transactionReceive, - transactionReceive.amount, - ) - - // trigger to send transaction via dlt-connector - void sendTransactionsToDltConnector() - } catch (e) { - await queryRunner.rollbackTransaction() - throw new LogError('Transaction was not successful', e) - } finally { - await queryRunner.release() - } - void sendTransactionReceivedEmail({ - firstName: recipient.firstName, - lastName: recipient.lastName, - email: recipient.emailContact.email, - language: recipient.language, - senderFirstName: sender.firstName, - senderLastName: sender.lastName, - senderEmail: sender.emailContact.email, - transactionAmount: amount, - }) - if (transactionLink) { - void sendTransactionLinkRedeemedEmail({ - firstName: sender.firstName, - lastName: sender.lastName, - email: sender.emailContact.email, - language: sender.language, - senderFirstName: recipient.firstName, - senderLastName: recipient.lastName, - senderEmail: recipient.emailContact.email, - transactionAmount: amount, - transactionMemo: memo, - }) - } - logger.info(`finished executeTransaction successfully`) - } finally { - releaseLock() - } - return true -} -*/ - @Resolver() export class TransactionResolver { @Authorized([RIGHTS.TRANSACTION_LIST]) diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 1cd443cd4..a7cf4c9f4 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -39,6 +39,23 @@ export async function processXComPendingSendCoins( sender, recipientIdentifier, ) + const openSenderPendingTx = await DbPendingTransaction.count({ + where: [ + { userGradidoID: sender.gradidoID, state: PendingTransactionState.NEW }, + { linkedUserGradidoID: sender.gradidoID, state: PendingTransactionState.NEW }, + ], + }) + const openReceiverPendingTx = await DbPendingTransaction.count({ + where: [ + { userGradidoID: recipientIdentifier, state: PendingTransactionState.NEW }, + { linkedUserGradidoID: recipientIdentifier, state: PendingTransactionState.NEW }, + ], + }) + if (openSenderPendingTx > 0 || openReceiverPendingTx > 0) { + throw new LogError( + `There exist still ongoing 'Pending-Transactions' for the involved users on sender-side!`, + ) + } // first calculate the sender balance and check if the transaction is allowed const senderBalance = await calculateSenderBalance(sender.id, amount.mul(-1), creationDate) if (!senderBalance) { @@ -77,10 +94,10 @@ export async function processXComPendingSendCoins( try { const pendingTx = DbPendingTransaction.create() pendingTx.amount = amount.mul(-1) - pendingTx.balance = senderBalance ? senderBalance.balance : new Decimal(0) + pendingTx.balance = senderBalance.balance pendingTx.balanceDate = creationDate - pendingTx.decay = senderBalance ? senderBalance.decay.decay : new Decimal(0) - pendingTx.decayStart = senderBalance ? senderBalance.decay.start : null + pendingTx.decay = senderBalance.decay.decay + pendingTx.decayStart = senderBalance.decay.start if (receiverCom.communityUuid) { pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid } @@ -95,7 +112,7 @@ export async function processXComPendingSendCoins( pendingTx.state = PendingTransactionState.NEW pendingTx.typeId = TransactionTypeId.SEND if (senderCom.communityUuid) pendingTx.userCommunityUuid = senderCom.communityUuid - pendingTx.id = sender.id + pendingTx.userId = sender.id pendingTx.userGradidoID = sender.gradidoID pendingTx.userName = fullName(sender.firstName, sender.lastName) logger.debug(`X-Com: initialized sender pendingTX=`, pendingTx) @@ -126,6 +143,7 @@ export async function processXComPendingSendCoins( voteResult, ) } + return voteResult } } catch (err) { throw new LogError(`Error:`, err) @@ -156,12 +174,11 @@ export async function processXComCommittingSendCoins( ) // first find pending Tx with given parameters const pendingTx = await DbPendingTransaction.findOneBy({ - userCommunityUuid: senderCom.communityUuid ? senderCom.communityUuid : 'homeCom-UUID', + userCommunityUuid: senderCom.communityUuid ?? 'homeCom-UUID', userGradidoID: sender.gradidoID, userName: fullName(sender.firstName, sender.lastName), - linkedUserCommunityUuid: receiverCom.communityUuid - ? receiverCom.communityUuid - : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID, + linkedUserCommunityUuid: + receiverCom.communityUuid ?? CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID, linkedUserGradidoID: recipUuid, typeId: TransactionTypeId.SEND, state: PendingTransactionState.NEW, @@ -187,7 +204,7 @@ export async function processXComCommittingSendCoins( args.recipientUserIdentifier = pendingTx.linkedUserGradidoID } args.creationDate = pendingTx.balanceDate.toISOString() - args.amount = pendingTx.amount + args.amount = pendingTx.amount.mul(-1) args.memo = pendingTx.memo args.senderCommunityUuid = pendingTx.userCommunityUuid args.senderUserUuid = pendingTx.userGradidoID diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index e22276823..eb100c621 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -15,6 +15,7 @@ import { calculateSenderBalance } from '@/util/calculateSenderBalance' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { getLastTransaction } from './getLastTransaction' +import Decimal from 'decimal.js-light' export async function settlePendingSenderTransaction( homeCom: DbCommunity, @@ -73,15 +74,13 @@ export async function settlePendingSenderTransaction( pendingTx.amount, pendingTx.balanceDate, ) - if (sendBalance?.balance !== pendingTx.balance) { - throw new LogError( - `X-Com: Calculation-Error on receiver balance: receiveBalance=${sendBalance?.balance}, pendingTx.balance=${pendingTx.balance}`, - ) + if (!sendBalance) { + throw new LogError(`Sender has not enough GDD or amount is < 0', sendBalance`) } - transactionSend.balance = pendingTx.balance + transactionSend.balance = sendBalance?.balance ?? new Decimal(0) transactionSend.balanceDate = pendingTx.balanceDate - transactionSend.decay = pendingTx.decay - transactionSend.decayStart = pendingTx.decayStart + transactionSend.decay = sendBalance.decay.decay // pendingTx.decay + transactionSend.decayStart = sendBalance.decay.start // pendingTx.decayStart transactionSend.previous = pendingTx.previous transactionSend.linkedTransactionId = pendingTx.linkedTransactionId await queryRunner.manager.insert(dbTransaction, transactionSend) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index caa60c0d1..f2fe5a88f 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -78,12 +78,15 @@ export class SendCoinsResolver { try { const txDate = new Date(args.creationDate) const receiveBalance = await calculateRecipientBalance(receiverUser.id, args.amount, txDate) + if (!receiveBalance) { + throw new LogError('Receiver has not enough GDD or amount is < 0', receiveBalance) + } const pendingTx = DbPendingTransaction.create() pendingTx.amount = args.amount - pendingTx.balance = receiveBalance ? receiveBalance.balance : args.amount + pendingTx.balance = receiveBalance.balance pendingTx.balanceDate = txDate - pendingTx.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) - pendingTx.decayStart = receiveBalance ? receiveBalance.decay.start : null + pendingTx.decay = receiveBalance.decay.decay + pendingTx.decayStart = receiveBalance.decay.start pendingTx.creationDate = new Date() pendingTx.linkedUserCommunityUuid = args.senderCommunityUuid pendingTx.linkedUserGradidoID = args.senderUserUuid diff --git a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts index 5c522061c..106b2beb1 100644 --- a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts @@ -77,18 +77,13 @@ export async function settlePendingReceiveTransaction( pendingTx.amount, pendingTx.balanceDate, ) - if ( - receiveBalance !== null && - receiveBalance.balance.toString() !== pendingTx.balance.toString() - ) { - throw new LogError( - `X-Com: Calculation-Error on receiver balance: receiveBalance=${receiveBalance.balance}, pendingTx.balance=${pendingTx.balance}`, - ) + if (!receiveBalance) { + throw new LogError(`Receiver has not enough GDD or amount is < 0', sendBalance`) } - transactionReceive.balance = receiveBalance ? receiveBalance.balance : pendingTx.amount + transactionReceive.balance = receiveBalance.balance transactionReceive.balanceDate = pendingTx.balanceDate - transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) - transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null + transactionReceive.decay = receiveBalance.decay.decay + transactionReceive.decayStart = receiveBalance.decay.start transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null transactionReceive.linkedTransactionId = pendingTx.linkedTransactionId await queryRunner.manager.insert(dbTransaction, transactionReceive) From 2e326e123c85f05c55924357ea826b951b8ce0b3 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 22 Sep 2023 00:42:54 +0200 Subject: [PATCH 16/24] correct accidental checkin of log4j.config --- backend/log4js-config.json | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/log4js-config.json b/backend/log4js-config.json index 90111a5ba..1cbd4519c 100644 --- a/backend/log4js-config.json +++ b/backend/log4js-config.json @@ -121,7 +121,6 @@ { "appenders": [ - "out", "backend", "errors" ], From f4e2588d0ea119677d9332359f661a4717cd3d14 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 22 Sep 2023 01:24:19 +0200 Subject: [PATCH 17/24] remove check of receiver balance after calculation --- .../src/graphql/api/1_0/resolver/SendCoinsResolver.ts | 11 ++++------- .../api/1_0/util/settlePendingReceiveTransaction.ts | 9 +++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index f2fe5a88f..12e7eab20 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -8,13 +8,13 @@ import { LogError } from '@/server/LogError' import { PendingTransactionState } from '../enum/PendingTransactionState' import { TransactionTypeId } from '../enum/TransactionTypeId' import { calculateRecipientBalance } from '../util/calculateRecipientBalance' -import Decimal from 'decimal.js-light' import { fullName } from '@/graphql/util/fullName' import { settlePendingReceiveTransaction } from '../util/settlePendingReceiveTransaction' // import { checkTradingLevel } from '@/graphql/util/checkTradingLevel' import { revertSettledReceiveTransaction } from '../util/revertSettledReceiveTransaction' import { findUserByIdentifier } from '@/graphql/util/findUserByIdentifier' import { SendCoinsResult } from '../model/SendCoinsResult' +import Decimal from 'decimal.js-light' @Resolver() // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -78,15 +78,12 @@ export class SendCoinsResolver { try { const txDate = new Date(args.creationDate) const receiveBalance = await calculateRecipientBalance(receiverUser.id, args.amount, txDate) - if (!receiveBalance) { - throw new LogError('Receiver has not enough GDD or amount is < 0', receiveBalance) - } const pendingTx = DbPendingTransaction.create() pendingTx.amount = args.amount - pendingTx.balance = receiveBalance.balance + pendingTx.balance = receiveBalance ? receiveBalance.balance : args.amount pendingTx.balanceDate = txDate - pendingTx.decay = receiveBalance.decay.decay - pendingTx.decayStart = receiveBalance.decay.start + pendingTx.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) + pendingTx.decayStart = receiveBalance ? receiveBalance.decay.start : null pendingTx.creationDate = new Date() pendingTx.linkedUserCommunityUuid = args.senderCommunityUuid pendingTx.linkedUserGradidoID = args.senderUserUuid diff --git a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts index 106b2beb1..e0e600be9 100644 --- a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts @@ -77,13 +77,10 @@ export async function settlePendingReceiveTransaction( pendingTx.amount, pendingTx.balanceDate, ) - if (!receiveBalance) { - throw new LogError(`Receiver has not enough GDD or amount is < 0', sendBalance`) - } - transactionReceive.balance = receiveBalance.balance + transactionReceive.balance = receiveBalance ? receiveBalance.balance : pendingTx.amount transactionReceive.balanceDate = pendingTx.balanceDate - transactionReceive.decay = receiveBalance.decay.decay - transactionReceive.decayStart = receiveBalance.decay.start + transactionReceive.decay = receiveBalance ? receiveBalance.decay.decay : new Decimal(0) + transactionReceive.decayStart = receiveBalance ? receiveBalance.decay.start : null transactionReceive.previous = receiveBalance ? receiveBalance.lastTransactionId : null transactionReceive.linkedTransactionId = pendingTx.linkedTransactionId await queryRunner.manager.insert(dbTransaction, transactionReceive) From d5be82ad7eac961e08e8a58eb12e20b0d8df6eab Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 22 Sep 2023 19:53:12 +0200 Subject: [PATCH 18/24] insert in transactionList the remoteUser of X-Com-Tx --- backend/src/graphql/model/User.ts | 36 ++++++++++--------- .../graphql/resolver/TransactionResolver.ts | 30 ++++++++++++++-- .../src/graphql/resolver/util/communities.ts | 11 ++++++ .../util/settlePendingSenderTransaction.ts | 3 +- 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/backend/src/graphql/model/User.ts b/backend/src/graphql/model/User.ts index 9e4c0fdf9..3474c6901 100644 --- a/backend/src/graphql/model/User.ts +++ b/backend/src/graphql/model/User.ts @@ -5,24 +5,26 @@ import { KlickTipp } from './KlickTipp' @ObjectType() export class User { - constructor(user: dbUser) { - this.id = user.id - this.gradidoID = user.gradidoID - this.alias = user.alias - if (user.emailContact) { - this.emailChecked = user.emailContact.emailChecked + constructor(user: dbUser | null) { + if (user) { + this.id = user.id + this.gradidoID = user.gradidoID + this.alias = user.alias + if (user.emailContact) { + this.emailChecked = user.emailContact.emailChecked + } + this.firstName = user.firstName + this.lastName = user.lastName + this.deletedAt = user.deletedAt + this.createdAt = user.createdAt + this.language = user.language + this.publisherId = user.publisherId + this.roles = user.userRoles?.map((userRole) => userRole.role) ?? [] + this.klickTipp = null + this.hasElopage = null + this.hideAmountGDD = user.hideAmountGDD + this.hideAmountGDT = user.hideAmountGDT } - this.firstName = user.firstName - this.lastName = user.lastName - this.deletedAt = user.deletedAt - this.createdAt = user.createdAt - this.language = user.language - this.publisherId = user.publisherId - this.roles = user.userRoles?.map((userRole) => userRole.role) ?? [] - this.klickTipp = null - this.hasElopage = null - this.hideAmountGDD = user.hideAmountGDD - this.hideAmountGDT = user.hideAmountGDT } @Field(() => Int) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 73f998b43..a1811daec 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { isCommunityAuthenticated, isHomeCommunity } from './util/communities' +import { getCommunityName, isCommunityAuthenticated, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -249,12 +249,21 @@ export class TransactionResolver { // find involved users; I am involved const involvedUserIds: number[] = [user.id] + const involvedRemoteUsers: User[] = [] userTransactions.forEach((transaction: dbTransaction) => { if (transaction.linkedUserId && !involvedUserIds.includes(transaction.linkedUserId)) { involvedUserIds.push(transaction.linkedUserId) } + if (!transaction.linkedUserId && transaction.linkedUserGradidoID) { + const remoteUser = new User(null) + remoteUser.gradidoID = transaction.linkedUserGradidoID + remoteUser.firstName = transaction.linkedUserName + remoteUser.lastName = transaction.linkedUserCommunityUuid + involvedRemoteUsers.push(remoteUser) + } }) - logger.debug(`involvedUserIds=${involvedUserIds}`) + logger.debug(`involvedUserIds=`, involvedUserIds) + logger.debug(`involvedRemoteUsers=`, involvedRemoteUsers) // We need to show the name for deleted users for old transactions const involvedDbUsers = await dbUser.find({ @@ -263,7 +272,7 @@ export class TransactionResolver { relations: ['emailContact'], }) const involvedUsers = involvedDbUsers.map((u) => new User(u)) - logger.debug(`involvedUsers=${involvedUsers}`) + logger.debug(`involvedUsers=`, involvedUsers) const self = new User(user) const transactions: Transaction[] = [] @@ -333,10 +342,25 @@ export class TransactionResolver { // transactions userTransactions.forEach((userTransaction: dbTransaction) => { + /* const linkedUser = userTransaction.typeId === TransactionTypeId.CREATION ? communityUser : involvedUsers.find((u) => u.id === userTransaction.linkedUserId) + */ + let linkedUser: User | undefined + if (userTransaction.typeId === TransactionTypeId.CREATION) { + linkedUser = communityUser + logger.debug('CREATION-linkedUser=', linkedUser) + } else if (userTransaction.linkedUserId) { + linkedUser = involvedUsers.find((u) => u.id === userTransaction.linkedUserId) + logger.debug('local linkedUser=', linkedUser) + } else if (userTransaction.linkedUserCommunityUuid) { + linkedUser = involvedRemoteUsers.find( + (u) => u.gradidoID === userTransaction.linkedUserGradidoID, + ) + logger.debug('remote linkedUser=', linkedUser) + } transactions.push(new Transaction(userTransaction, self, linkedUser)) }) logger.debug(`TransactionTypeId.CREATION: transactions=${transactions}`) diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts index f55816229..52fee86b2 100644 --- a/backend/src/graphql/resolver/util/communities.ts +++ b/backend/src/graphql/resolver/util/communities.ts @@ -40,3 +40,14 @@ export async function isCommunityAuthenticated(communityIdentifier: string): Pro return false } } + +export async function getCommunityName(communityIdentifier: string): Promise { + const community = await DbCommunity.findOne({ + where: [{ communityUuid: communityIdentifier }, { url: communityIdentifier }], + }) + if (community?.name) { + return community.name + } else { + return '' + } +} diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index eb100c621..b9c7d8b36 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -7,6 +7,7 @@ import { Community as DbCommunity } from '@entity/Community' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' import { Transaction as dbTransaction } from '@entity/Transaction' import { User as DbUser } from '@entity/User' +import { Decimal } from 'decimal.js-light' import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' import { LogError } from '@/server/LogError' @@ -15,7 +16,6 @@ import { calculateSenderBalance } from '@/util/calculateSenderBalance' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { getLastTransaction } from './getLastTransaction' -import Decimal from 'decimal.js-light' export async function settlePendingSenderTransaction( homeCom: DbCommunity, @@ -66,6 +66,7 @@ export async function settlePendingSenderTransaction( transactionSend.userGradidoID = pendingTx.userGradidoID transactionSend.userName = pendingTx.userName transactionSend.linkedUserId = pendingTx.linkedUserId + transactionSend.linkedUserCommunityUuid = pendingTx.linkedUserCommunityUuid transactionSend.linkedUserGradidoID = pendingTx.linkedUserGradidoID transactionSend.linkedUserName = pendingTx.linkedUserName transactionSend.amount = pendingTx.amount From 90fca336d20c880b99afee18ec229c3e7094d8d9 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 28 Sep 2023 23:08:23 +0200 Subject: [PATCH 19/24] solve merge-conflict --- .../resolver/util/settlePendingSenderTransaction.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index ac9318f42..b9c7d8b36 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable new-cap */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -/* + import { getConnection } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' @@ -94,7 +94,7 @@ export async function settlePendingSenderTransaction( await queryRunner.commitTransaction() logger.info(`commit send Transaction successful...`) - --* + /* await EVENT_TRANSACTION_SEND(sender, recipient, transactionSend, transactionSend.amount) await EVENT_TRANSACTION_RECEIVE( @@ -103,7 +103,7 @@ export async function settlePendingSenderTransaction( transactionReceive, transactionReceive.amount, ) - *-- + */ // trigger to send transaction via dlt-connector // void sendTransactionsToDltConnector() } catch (e) { @@ -113,7 +113,7 @@ export async function settlePendingSenderTransaction( await queryRunner.release() releaseLock() } - --* + /* void sendTransactionReceivedEmail({ firstName: recipient.firstName, lastName: recipient.lastName, @@ -141,7 +141,6 @@ export async function settlePendingSenderTransaction( } finally { releaseLock() } - *-- + */ return true } -*/ From 9ddecd95acc1e1bedc577d085d722e7e51e8b423 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Mon, 2 Oct 2023 17:06:57 +0200 Subject: [PATCH 20/24] add test for X-Com sendCoins in TransactionResolver.test --- .../federation/client/1_0/SendCoinsClient.ts | 26 ++++---- .../resolver/TransactionResolver.test.ts | 62 ++++++++++++++++--- .../graphql/resolver/TransactionResolver.ts | 10 ++- .../resolver/util/processXComSendCoins.ts | 4 +- 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 3b3bf4f2c..fe22d036c 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -6,10 +6,10 @@ 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' -import { voteForSendCoins } from './query/voteForSendCoins' +import { revertSendCoins as revertSendCoinsQuery } from './query/revertSendCoins' +import { revertSettledSendCoins as revertSettledSendCoinsQuery } from './query/revertSettledSendCoins' +import { settleSendCoins as settleSendCoinsQuery } from './query/settleSendCoins' +import { voteForSendCoins as voteForSendCoinsQuery } from './query/voteForSendCoins' // eslint-disable-next-line camelcase export class SendCoinsClient { @@ -31,16 +31,16 @@ export class SendCoinsClient { }) } - voteForSendCoins = async (args: SendCoinsArgs): Promise => { + async voteForSendCoins(args: SendCoinsArgs): Promise { logger.debug('X-Com: voteForSendCoins against endpoint=', this.endpoint) try { logger.debug(`X-Com: SendCoinsClient: voteForSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { data } = await this.client.rawRequest(voteForSendCoins, { args }) + const { data } = await this.client.rawRequest(voteForSendCoinsQuery, { args }) logger.debug(`X-Com: SendCoinsClient: after rawRequest...data:`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!data?.voteForSendCoins?.vote) { - logger.warn('X-Com: voteForSendCoins failed with: ', data) + logger.debug('X-Com: voteForSendCoins failed with: ', data) return new SendCoinsResult() } const result = new SendCoinsResult() @@ -56,12 +56,12 @@ export class SendCoinsClient { } } - revertSendCoins = async (args: SendCoinsArgs): Promise => { + async revertSendCoins(args: SendCoinsArgs): Promise { logger.debug('X-Com: revertSendCoins against endpoint=', this.endpoint) try { logger.debug(`X-Com: SendCoinsClient: revertSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { data } = await this.client.rawRequest(revertSendCoins, { args }) + const { data } = await this.client.rawRequest(revertSendCoinsQuery, { args }) logger.debug(`X-Com: SendCoinsClient: after revertSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!data?.revertSendCoins) { @@ -81,12 +81,12 @@ export class SendCoinsClient { } } - settleSendCoins = async (args: SendCoinsArgs): Promise => { + async settleSendCoins(args: SendCoinsArgs): Promise { logger.debug(`X-Com: settleSendCoins against endpoint='${this.endpoint}'...`) try { logger.debug(`X-Com: SendCoinsClient: settleSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { data } = await this.client.rawRequest(settleSendCoins, { args }) + const { data } = await this.client.rawRequest(settleSendCoinsQuery, { args }) logger.debug(`X-Com: SendCoinsClient: after settleSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!data?.settleSendCoins) { @@ -108,12 +108,12 @@ export class SendCoinsClient { } } - revertSettledSendCoins = async (args: SendCoinsArgs): Promise => { + async revertSettledSendCoins(args: SendCoinsArgs): Promise { logger.debug(`X-Com: revertSettledSendCoins against endpoint='${this.endpoint}'...`) try { logger.debug(`X-Com: SendCoinsClient: revertSettledSendCoins with args=`, args) // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { data } = await this.client.rawRequest(revertSettledSendCoins, { args }) + const { data } = await this.client.rawRequest(revertSettledSendCoinsQuery, { args }) logger.debug(`X-Com: SendCoinsClient: after revertSettledSendCoins: data=`, data) // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (!data?.revertSettledSendCoins) { diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 20765e952..e115387cc 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -1,10 +1,14 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ import { Connection, In } from '@dbTools/typeorm' import { Community as DbCommunity } from '@entity/Community' import { DltTransaction } from '@entity/DltTransaction' import { Event as DbEvent } from '@entity/Event' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { Transaction } from '@entity/Transaction' import { User } from '@entity/User' import { ApolloServerTestClient } from 'apollo-server-testing' @@ -14,6 +18,9 @@ import { cleanDB, testEnvironment } from '@test/helpers' import { logger } from '@test/testSetup' import { EventType } from '@/event/Events' +import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs' +import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' +import { SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient' import { userFactory } from '@/seeds/factory/user' import { confirmContribution, @@ -27,6 +34,7 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' +import { fullName } from '@/util/utilities' let mutate: ApolloServerTestClient['mutate'], con: Connection let query: ApolloServerTestClient['query'] @@ -47,7 +55,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDB() - await con.close() + await con.destroy() // close() }) let bobData: any @@ -59,6 +67,7 @@ let peter: User let homeCom: DbCommunity let foreignCom: DbCommunity +let fedForeignCom: DbFederatedCommunity describe('send coins', () => { beforeAll(async () => { @@ -67,7 +76,7 @@ describe('send coins', () => { await userFactory(testEnv, stephenHawking) await userFactory(testEnv, garrickOllivander) homeCom = DbCommunity.create() - homeCom.communityUuid = '7f474922-b6d8-4b64-8cd0-ebf0a1d8756e' + homeCom.communityUuid = '7f474922-b6d8-4b64-8cd0-ebf0a1d875aa' homeCom.creationDate = new Date('2000-01-01') homeCom.description = 'homeCom description' homeCom.foreign = false @@ -78,14 +87,15 @@ describe('send coins', () => { homeCom = await DbCommunity.save(homeCom) foreignCom = DbCommunity.create() - foreignCom.communityUuid = '7f474922-b6d8-4b64-8cd0-cea0a1d8756e' + foreignCom.communityUuid = '7f474922-b6d8-4b64-8cd0-cea0a1d875bb' foreignCom.creationDate = new Date('2000-06-06') - foreignCom.description = 'homeCom description' + foreignCom.description = 'foreignCom 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.url = 'foreignCom_url' + foreignCom.authenticatedAt = new Date('2000-06-12') foreignCom = await DbCommunity.save(foreignCom) bobData = { @@ -594,7 +604,45 @@ describe('send coins', () => { }) }) - describe.only('X-Com send coins via gradido ID', () => { + describe('X-Com send coins via gradido ID', () => { + beforeAll(async () => { + fedForeignCom = DbFederatedCommunity.create() + fedForeignCom.apiVersion = '1_0' + fedForeignCom.foreign = true + fedForeignCom.publicKey = Buffer.from('foreignCom publicKey') + fedForeignCom.endPoint = 'http://foreignCom_url/api' + fedForeignCom.lastAnnouncedAt = new Date('2000-06-09') + fedForeignCom.verifiedAt = new Date('2000-06-10') + fedForeignCom = await DbFederatedCommunity.save(fedForeignCom) + + jest + .spyOn(SendCoinsClient.prototype, 'voteForSendCoins') + .mockImplementation(async (args: SendCoinsArgs): Promise => { + // console.log('mock of voteForSendCoins...', args) + return Promise.resolve({ + vote: true, + recipName: fullName(peter.firstName, peter.lastName), + recipGradidoID: args.recipientUserIdentifier, + }) + }) + + jest + .spyOn(SendCoinsClient.prototype, 'settleSendCoins') + .mockImplementation(async (args: SendCoinsArgs): Promise => { + // console.log('mock of settleSendCoins...', args) + return Promise.resolve(true) + }) + + await mutate({ + mutation: login, + variables: bobData, + }) + }) + + afterAll(() => { + jest.clearAllMocks() + }) + it('sends the coins', async () => { await expect( mutate({ diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index a1811daec..990939119 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -38,7 +38,7 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' -import { getCommunityName, isCommunityAuthenticated, isHomeCommunity } from './util/communities' +import { isCommunityAuthenticated, isHomeCommunity } from './util/communities' import { findUserByIdentifier } from './util/findUserByIdentifier' import { getLastTransaction } from './util/getLastTransaction' import { getTransactionList } from './util/getTransactionList' @@ -258,7 +258,7 @@ export class TransactionResolver { const remoteUser = new User(null) remoteUser.gradidoID = transaction.linkedUserGradidoID remoteUser.firstName = transaction.linkedUserName - remoteUser.lastName = transaction.linkedUserCommunityUuid + remoteUser.lastName = 'GradidoID: ' + transaction.linkedUserGradidoID involvedRemoteUsers.push(remoteUser) } }) @@ -385,7 +385,7 @@ export class TransactionResolver { { recipientCommunityIdentifier, recipientIdentifier, amount, memo }: TransactionSendArgs, @Ctx() context: Context, ): Promise { - logger.info( + logger.debug( `sendCoins(recipientCommunityIdentifier=${recipientCommunityIdentifier}, recipientIdentifier=${recipientIdentifier}, amount=${amount}, memo=${memo})`, ) const homeCom = await DbCommunity.findOneOrFail({ where: { foreign: false } }) @@ -413,6 +413,7 @@ export class TransactionResolver { const recipCom = await DbCommunity.findOneOrFail({ where: { communityUuid: recipientCommunityIdentifier }, }) + logger.debug('recipient commuity: ', recipCom) let pendingResult: SendCoinsResult let committingResult: SendCoinsResult const creationDate = new Date() @@ -427,7 +428,9 @@ export class TransactionResolver { senderUser, recipientIdentifier, ) + logger.debug('processXComPendingSendCoins result: ', pendingResult) if (pendingResult.vote && pendingResult.recipGradidoID) { + logger.debug('vor processXComCommittingSendCoins... ') committingResult = await processXComCommittingSendCoins( recipCom, homeCom, @@ -437,6 +440,7 @@ export class TransactionResolver { senderUser, pendingResult.recipGradidoID, ) + logger.debug('processXComCommittingSendCoins result: ', committingResult) if (!committingResult.vote) { logger.fatal('FATAL ERROR: on processXComCommittingSendCoins for', committingResult) throw new LogError( diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index d51d75156..10efe2736 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -87,7 +87,7 @@ export async function processXComPendingSendCoins( args.senderUserName = fullName(sender.firstName, sender.lastName) logger.debug(`X-Com: ready for voteForSendCoins with args=`, args) voteResult = await client.voteForSendCoins(args) - logger.debug(`X-Com: returnd from voteForSendCoins:`, voteResult) + logger.debug(`X-Com: returned from voteForSendCoins:`, voteResult) if (voteResult.vote) { logger.debug(`X-Com: prepare pendingTransaction for sender...`) // writing the pending transaction on receiver-side was successfull, so now write the sender side @@ -138,7 +138,7 @@ export async function processXComPendingSendCoins( } logger.debug(`voteForSendCoins()-1_0... successfull`) } else { - logger.debug( + logger.error( `X-Com: break with error on writing pendingTransaction for recipient...`, voteResult, ) From c0a09c2a2854b96fbd63e500b4950d8da952a05b Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 4 Oct 2023 19:09:13 +0200 Subject: [PATCH 21/24] extent voteSendCoins result and increase coverage for backend and federation --- backend/jest.config.js | 2 +- .../federation/client/1_0/SendCoinsClient.ts | 6 +++- .../client/1_0/model/SendCoinsResult.ts | 8 ++++- .../client/1_0/query/voteForSendCoins.ts | 34 ++----------------- .../resolver/TransactionResolver.test.ts | 9 ++--- .../graphql/resolver/TransactionResolver.ts | 2 +- .../resolver/util/processXComSendCoins.ts | 15 ++++++-- federation/jest.config.js | 2 +- .../graphql/api/1_0/model/SendCoinsResult.ts | 8 ++++- .../1_0/resolver/SendCoinsResolver.test.ts | 8 +++-- .../api/1_0/resolver/SendCoinsResolver.ts | 6 ++-- 11 files changed, 52 insertions(+), 48 deletions(-) diff --git a/backend/jest.config.js b/backend/jest.config.js index 8b322d76c..656265bc7 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -7,7 +7,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 85, + lines: 88, }, }, setupFiles: ['/test/testSetup.ts'], diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index fe22d036c..c96961103 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -48,7 +48,11 @@ export class SendCoinsClient { // 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 + result.recipFirstName = data.voteForSendCoins.recipFirstName + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment + result.recipLastName = data.voteForSendCoins.recipLastName + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment + result.recipAlias = data.voteForSendCoins.recipAlias logger.debug('X-Com: voteForSendCoins successful with result=', result) return result } catch (err) { diff --git a/backend/src/federation/client/1_0/model/SendCoinsResult.ts b/backend/src/federation/client/1_0/model/SendCoinsResult.ts index 930d22ff5..8fc21305f 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsResult.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsResult.ts @@ -13,5 +13,11 @@ export class SendCoinsResult { recipGradidoID: string | null @Field(() => String, { nullable: true }) - recipName: string | null + recipFirstName: string | null + + @Field(() => String, { nullable: true }) + recipLastName: string | null + + @Field(() => String, { nullable: true }) + recipAlias: string | null } diff --git a/backend/src/federation/client/1_0/query/voteForSendCoins.ts b/backend/src/federation/client/1_0/query/voteForSendCoins.ts index a5456bec7..d2550eb1a 100644 --- a/backend/src/federation/client/1_0/query/voteForSendCoins.ts +++ b/backend/src/federation/client/1_0/query/voteForSendCoins.ts @@ -5,37 +5,9 @@ export const voteForSendCoins = gql` voteForSendCoins(data: $args) { vote recipGradidoID - recipName + recipFirstName + recipLastName + recipAlias } } ` - -/* - mutation ( - $recipientCommunityUuid: String! - $recipientUserIdentifier: String! - $creationDate: String! - $amount: Decimal! - $memo: String! - $senderCommunityUuid: String! - $senderUserUuid: String! - $senderUserName: String! - ) { - voteForSendCoins( - data: { - recipientCommunityUuid: $recipientCommunityUuid - recipientUserIdentifier: $recipientUserIdentifier - creationDate: $creationDate - amount: $amount - memo: $memo - senderCommunityUuid: $senderCommunityUuid - senderUserUuid: $senderUserUuid - senderUserName: $senderUserName - } - ) { - vote - recipGradidoID - recipName - } - } -*/ diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index e115387cc..0bb05ca76 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -34,7 +34,6 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' -import { fullName } from '@/util/utilities' let mutate: ApolloServerTestClient['mutate'], con: Connection let query: ApolloServerTestClient['query'] @@ -618,18 +617,20 @@ describe('send coins', () => { jest .spyOn(SendCoinsClient.prototype, 'voteForSendCoins') .mockImplementation(async (args: SendCoinsArgs): Promise => { - // console.log('mock of voteForSendCoins...', args) + logger.debug('mock of voteForSendCoins...', args) return Promise.resolve({ vote: true, - recipName: fullName(peter.firstName, peter.lastName), + recipFirstName: peter.firstName, + recipLastName: peter.lastName, recipGradidoID: args.recipientUserIdentifier, + recipAlias: peter.alias, }) }) jest .spyOn(SendCoinsClient.prototype, 'settleSendCoins') .mockImplementation(async (args: SendCoinsArgs): Promise => { - // console.log('mock of settleSendCoins...', args) + logger.debug('mock of settleSendCoins...', args) return Promise.resolve(true) }) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 990939119..66a889798 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -258,7 +258,7 @@ export class TransactionResolver { const remoteUser = new User(null) remoteUser.gradidoID = transaction.linkedUserGradidoID remoteUser.firstName = transaction.linkedUserName - remoteUser.lastName = 'GradidoID: ' + transaction.linkedUserGradidoID + remoteUser.lastName = '(GradidoID: ' + transaction.linkedUserGradidoID + ')' involvedRemoteUsers.push(remoteUser) } }) diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 10efe2736..af6826bd1 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -104,8 +104,8 @@ export async function processXComPendingSendCoins( if (voteResult.recipGradidoID) { pendingTx.linkedUserGradidoID = voteResult.recipGradidoID } - if (voteResult.recipName) { - pendingTx.linkedUserName = voteResult.recipName + if (voteResult.recipFirstName && voteResult.recipLastName) { + pendingTx.linkedUserName = fullName(voteResult.recipFirstName, voteResult.recipLastName) } pendingTx.memo = memo pendingTx.previous = senderBalance ? senderBalance.lastTransactionId : null @@ -224,7 +224,16 @@ export async function processXComCommittingSendCoins( pendingTx, ) if (sendCoinsResult.vote) { - sendCoinsResult.recipName = pendingTx.linkedUserName + if (pendingTx.linkedUserName) { + sendCoinsResult.recipFirstName = pendingTx.linkedUserName.slice( + 0, + pendingTx.linkedUserName.indexOf(' '), + ) + sendCoinsResult.recipLastName = pendingTx.linkedUserName.slice( + pendingTx.linkedUserName.indexOf(' '), + pendingTx.linkedUserName.length, + ) + } sendCoinsResult.recipGradidoID = pendingTx.linkedUserGradidoID } } catch (err) { diff --git a/federation/jest.config.js b/federation/jest.config.js index f859bbe2f..797a5847e 100644 --- a/federation/jest.config.js +++ b/federation/jest.config.js @@ -6,7 +6,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 75, + lines: 77, }, }, setupFiles: ['/test/testSetup.ts'], diff --git a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts index 930d22ff5..8fc21305f 100644 --- a/federation/src/graphql/api/1_0/model/SendCoinsResult.ts +++ b/federation/src/graphql/api/1_0/model/SendCoinsResult.ts @@ -13,5 +13,11 @@ export class SendCoinsResult { recipGradidoID: string | null @Field(() => String, { nullable: true }) - recipName: string | null + recipFirstName: string | null + + @Field(() => String, { nullable: true }) + recipLastName: string | null + + @Field(() => String, { nullable: true }) + recipAlias: string | null } diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts index 83e502a03..412786fa6 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -50,7 +50,9 @@ describe('SendCoinsResolver', () => { voteForSendCoins(data: $args) { vote recipGradidoID - recipName + recipFirstName + recipLastName + recipAlias } }` const settleSendCoinsMutation = ` @@ -204,7 +206,9 @@ describe('SendCoinsResolver', () => { data: { voteForSendCoins: { recipGradidoID: '56a55482-909e-46a4-bfa2-cd025e894ebd', - recipName: 'recipUser-FirstName recipUser-LastName', + recipFirstName: 'recipUser-FirstName', + recipLastName: 'recipUser-LastName', + recipAlias: 'recipUser-alias', vote: true, }, }, diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index d942c48f3..ef37c0d00 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -99,8 +99,10 @@ export class SendCoinsResolver { await DbPendingTransaction.insert(pendingTx) result.vote = true - result.recipName = pendingTx.userName - result.recipGradidoID = pendingTx.userGradidoID + result.recipFirstName = receiverUser.firstName + result.recipLastName = receiverUser.lastName + result.recipAlias = receiverUser.alias + result.recipGradidoID = receiverUser.gradidoID logger.debug(`voteForSendCoins()-1_0... successfull`) } catch (err) { throw new LogError(`Error in voteForSendCoins: `, err) From b954ad27eab95006b6b4d6043ae4c8c42282d121 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Wed, 4 Oct 2023 23:50:22 +0200 Subject: [PATCH 22/24] reduce backend coverage again --- backend/jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/jest.config.js b/backend/jest.config.js index 656265bc7..625dca00f 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -7,7 +7,7 @@ module.exports = { collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], coverageThreshold: { global: { - lines: 88, + lines: 84, }, }, setupFiles: ['/test/testSetup.ts'], From bb2871ae66cfea33b4475cc4c340acb791b3d088 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 5 Oct 2023 00:08:46 +0200 Subject: [PATCH 23/24] add CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED=true in X-Com-Test --- backend/src/graphql/resolver/TransactionResolver.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 0bb05ca76..846060914 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -34,6 +34,7 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' +import { CONFIG } from '@/config' let mutate: ApolloServerTestClient['mutate'], con: Connection let query: ApolloServerTestClient['query'] @@ -605,6 +606,7 @@ describe('send coins', () => { describe('X-Com send coins via gradido ID', () => { beforeAll(async () => { + CONFIG.FEDERATION_XCOM_SENDCOINS_ENABLED = true fedForeignCom = DbFederatedCommunity.create() fedForeignCom.apiVersion = '1_0' fedForeignCom.foreign = true From 5fd48fd4915dd9c257f52b2aa33e19238d59365e Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 5 Oct 2023 00:17:19 +0200 Subject: [PATCH 24/24] linting --- backend/src/graphql/resolver/TransactionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 846060914..857159a97 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -17,6 +17,7 @@ import { GraphQLError } from 'graphql' import { cleanDB, testEnvironment } from '@test/helpers' import { logger } from '@test/testSetup' +import { CONFIG } from '@/config' import { EventType } from '@/event/Events' import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs' import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' @@ -34,7 +35,6 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' -import { CONFIG } from '@/config' let mutate: ApolloServerTestClient['mutate'], con: Connection let query: ApolloServerTestClient['query']