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/backend/jest.config.js b/backend/jest.config.js index 8b322d76c..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: 85, + lines: 84, }, }, setupFiles: ['/test/testSetup.ts'], diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 7c849f7d9..a77840738 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-08-25', + EXPECTED: 'v20.2023-09-19', CURRENT: '', }, } @@ -122,8 +122,11 @@ 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: + process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' || false, // default value for community-uuid is equal uuid of stage-3 FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID: process.env.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID ?? '56a55482-909e-46a4-bfa2-cd025e894ebc', diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 3b3bf4f2c..c96961103 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() @@ -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) { @@ -56,12 +60,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 +85,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 +112,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/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/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.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index bd7d0f2a8..857159a97 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -1,9 +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' @@ -12,7 +17,11 @@ 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' +import { SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient' import { userFactory } from '@/seeds/factory/user' import { confirmContribution, @@ -46,7 +55,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDB() - await con.close() + await con.destroy() // close() }) let bobData: any @@ -56,12 +65,38 @@ let user: User[] let bob: User let peter: User +let homeCom: DbCommunity +let foreignCom: DbCommunity +let fedForeignCom: DbFederatedCommunity + 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 = '7f474922-b6d8-4b64-8cd0-ebf0a1d875aa' + 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) + + foreignCom = DbCommunity.create() + foreignCom.communityUuid = '7f474922-b6d8-4b64-8cd0-cea0a1d875bb' + foreignCom.creationDate = new Date('2000-06-06') + 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.authenticatedAt = new Date('2000-06-12') + foreignCom = await DbCommunity.save(foreignCom) bobData = { email: 'bob@baumeister.de', @@ -91,6 +126,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'wrong@email.com', amount: 100, memo: 'test test', @@ -119,6 +155,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'stephen@hawking.uk', amount: 100, memo: 'test test', @@ -148,6 +185,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'garrick@ollivander.com', amount: 100, memo: 'test test', @@ -184,6 +222,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'bob@baumeister.de', amount: 100, memo: 'test test', @@ -207,6 +246,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 +278,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 +311,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 100, memo: 'testing', @@ -319,6 +361,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 +393,7 @@ describe('send coins', () => { await mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 50, memo: 'unrepeatable memo', @@ -456,6 +500,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: peter?.gradidoID, amount: 10, memo: 'send via gradido ID', @@ -496,6 +541,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'bob', amount: 6.66, memo: 'send via alias', @@ -558,12 +604,75 @@ 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 + 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 => { + logger.debug('mock of voteForSendCoins...', args) + return Promise.resolve({ + vote: true, + recipFirstName: peter.firstName, + recipLastName: peter.lastName, + recipGradidoID: args.recipientUserIdentifier, + recipAlias: peter.alias, + }) + }) + + jest + .spyOn(SendCoinsClient.prototype, 'settleSendCoins') + .mockImplementation(async (args: SendCoinsArgs): Promise => { + logger.debug('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({ + 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( mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 10, memo: 'first transaction', @@ -580,6 +689,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 20, memo: 'second transaction', @@ -596,6 +706,7 @@ describe('send coins', () => { mutate({ mutation: sendCoins, variables: { + recipientCommunityIdentifier: homeCom.communityUuid, recipientIdentifier: 'peter@lustig.de', amount: 30, memo: 'third transaction', @@ -612,6 +723,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/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index f368abca3..66a889798 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -3,6 +3,7 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { getConnection, In, IsNull } from '@dbTools/typeorm' +import { Community as DbCommunity } from '@entity/Community' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' import { Transaction as dbTransaction } from '@entity/Transaction' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' @@ -20,11 +21,13 @@ import { TransactionList } from '@model/TransactionList' import { User } from '@model/User' import { RIGHTS } from '@/auth/RIGHTS' +import { CONFIG } from '@/config' import { sendTransactionLinkRedeemedEmail, 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' @@ -35,9 +38,14 @@ import { calculateBalance } from '@/util/validate' import { virtualLinkTransaction, virtualDecayTransaction } from '@/util/virtualTransactions' import { BalanceResolver } from './BalanceResolver' +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 { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' import { transactionLinkSummary } from './util/transactionLinkSummary' @@ -66,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) { @@ -239,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 = '(GradidoID: ' + transaction.linkedUserGradidoID + ')' + 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({ @@ -253,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[] = [] @@ -323,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}`) @@ -351,19 +385,84 @@ 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 } }) const senderUser = getUser(context) - // validate recipient user - const recipientUser = await findUserByIdentifier(recipientIdentifier) - if (!recipientUser) { - throw new LogError('The recipient user was not found', recipientUser) - } + if (!recipientCommunityIdentifier || (await isHomeCommunity(recipientCommunityIdentifier))) { + // processing sendCoins within sender and recepient are both in home community + // validate recipient user + const recipientUser = await findUserByIdentifier(recipientIdentifier) + 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) + await executeTransaction(amount, memo, senderUser, recipientUser) + logger.info('successful executeTransaction', amount, memo, senderUser, recipientUser) + } else { + // processing a x-community sendCoins + logger.debug('X-Com: 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!') + } + const recipCom = await DbCommunity.findOneOrFail({ + where: { communityUuid: recipientCommunityIdentifier }, + }) + logger.debug('recipient commuity: ', recipCom) + let pendingResult: SendCoinsResult + let committingResult: SendCoinsResult + const creationDate = new Date() + + try { + pendingResult = await processXComPendingSendCoins( + recipCom, + homeCom, + creationDate, + amount, + memo, + senderUser, + recipientIdentifier, + ) + logger.debug('processXComPendingSendCoins result: ', pendingResult) + if (pendingResult.vote && pendingResult.recipGradidoID) { + logger.debug('vor processXComCommittingSendCoins... ') + committingResult = await processXComCommittingSendCoins( + recipCom, + homeCom, + creationDate, + amount, + memo, + senderUser, + pendingResult.recipGradidoID, + ) + logger.debug('processXComCommittingSendCoins result: ', committingResult) + 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/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', diff --git a/backend/src/graphql/resolver/util/communities.ts b/backend/src/graphql/resolver/util/communities.ts new file mode 100644 index 000000000..52fee86b2 --- /dev/null +++ b/backend/src/graphql/resolver/util/communities.ts @@ -0,0 +1,53 @@ +import { Community as DbCommunity } from '@entity/Community' + +export async function isHomeCommunity(communityIdentifier: string): Promise { + 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 + } +} + +export async function getCommunityUrl(communityIdentifier: string): Promise { + const community = await DbCommunity.findOneOrFail({ + where: [ + { communityUuid: communityIdentifier }, + { name: communityIdentifier }, + { url: communityIdentifier }, + ], + }) + 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 + } +} + +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/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 5befe2192..af6826bd1 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -1,4 +1,3 @@ -/* import { Community as DbCommunity } from '@entity/Community' import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' @@ -7,6 +6,7 @@ import { Decimal } from 'decimal.js-light' import { CONFIG } from '@/config' import { SendCoinsArgs } from '@/federation/client/1_0/model/SendCoinsArgs' +import { SendCoinsResult } from '@/federation/client/1_0/model/SendCoinsResult' // eslint-disable-next-line camelcase import { SendCoinsClient as V1_0_SendCoinsClient } from '@/federation/client/1_0/SendCoinsClient' import { SendCoinsClientFactory } from '@/federation/client/SendCoinsClientFactory' @@ -20,28 +20,42 @@ 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, -): Promise { - + recipientIdentifier: string, +): Promise { + let voteResult: SendCoinsResult try { logger.debug( `XCom: processXComPendingSendCoins...`, - receiverFCom, receiverCom, senderCom, - creationDate, amount, memo, sender, - recipient, + 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) { @@ -49,42 +63,56 @@ 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 sendCoinsResult = await client.voteForSendCoins(args) - logger.debug(`X-Com: returnd from voteForSendCoins:`, sendCoinsResult) - if (sendCoinsResult) { + voteResult = await client.voteForSendCoins(args) + 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 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.linkedUserCommunityUuid = receiverCom.communityUuid - ? receiverCom.communityUuid - : CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID - pendingTx.linkedUserGradidoID = recipient.gradidoID - pendingTx.linkedUserName = sendCoinsResult.recipName + if (receiverCom.communityUuid) { + pendingTx.linkedUserCommunityUuid = receiverCom.communityUuid + } + if (voteResult.recipGradidoID) { + pendingTx.linkedUserGradidoID = voteResult.recipGradidoID + } + if (voteResult.recipFirstName && voteResult.recipLastName) { + pendingTx.linkedUserName = fullName(voteResult.recipFirstName, voteResult.recipLastName) + } 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.userId = sender.id pendingTx.userGradidoID = sender.gradidoID pendingTx.userName = fullName(sender.firstName, sender.lastName) logger.debug(`X-Com: initialized sender pendingTX=`, pendingTx) @@ -109,45 +137,49 @@ export async function processXComPendingSendCoins( ) } logger.debug(`voteForSendCoins()-1_0... successfull`) + } else { + logger.error( + `X-Com: break with error on writing pendingTransaction for recipient...`, + voteResult, + ) } + return voteResult } } catch (err) { - logger.error(`Error:`, err) + throw new LogError(`Error:`, err) } - return true + return new SendCoinsResult() } export async function processXComCommittingSendCoins( - receiverFCom: DbFederatedCommunity, receiverCom: DbCommunity, senderCom: DbCommunity, creationDate: Date, amount: Decimal, memo: string, sender: dbUser, - recipient: dbUser, -): Promise { + recipUuid: string, +): Promise { + const sendCoinsResult = new SendCoinsResult() try { logger.debug( `XCom: processXComCommittingSendCoins...`, - receiverFCom, receiverCom, senderCom, creationDate, amount, memo, sender, - recipient, + recipUuid, ) // 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, - linkedUserGradidoID: recipient.gradidoID, + linkedUserCommunityUuid: + receiverCom.communityUuid ?? CONFIG.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID, + linkedUserGradidoID: recipUuid, typeId: TransactionTypeId.SEND, state: PendingTransactionState.NEW, balanceDate: creationDate, @@ -155,6 +187,13 @@ export async function processXComCommittingSendCoins( }) if (pendingTx) { logger.debug(`X-Com: find pending Tx for settlement:`, pendingTx) + const receiverFCom = await DbFederatedCommunity.findOneOrFail({ + where: { + publicKey: receiverCom.publicKey, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + apiVersion: CONFIG.FEDERATION_BACKEND_SEND_ON_API, + }, + }) const client = SendCoinsClientFactory.getInstance(receiverFCom) // eslint-disable-next-line camelcase if (client instanceof V1_0_SendCoinsClient) { @@ -166,7 +205,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 @@ -179,7 +218,24 @@ export async function processXComCommittingSendCoins( 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) { + 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) { logger.error(`Error in writing sender pending transaction: `, err) // revert the existing pending transaction on receiver side @@ -205,7 +261,7 @@ export async function processXComCommittingSendCoins( } } catch (err) { logger.error(`Error:`, err) + sendCoinsResult.vote = false } - return true + return sendCoinsResult } -*/ diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index c10855f93..b9c7d8b36 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -1,12 +1,13 @@ /* 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' 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' @@ -65,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 @@ -73,15 +75,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) @@ -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 } -*/ diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 965b52bec..305af4939 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -78,9 +78,31 @@ export const sendActivationEmail = gql` } ` +/* export const sendCoins = gql` - mutation ($recipientIdentifier: String!, $amount: Decimal!, $memo: String!) { - sendCoins(recipientIdentifier: $recipientIdentifier, amount: $amount, memo: $memo) + mutation ($identifier: String!, $amount: Decimal!, $memo: String!, $communityIdentifier: String) { + sendCoins( + identifier: $identifier + amount: $amount + memo: $memo + communityIdentifier: $communityIdentifier + ) + } +` +*/ +export const sendCoins = gql` + mutation ( + $recipientCommunityIdentifier: String! + $recipientIdentifier: String! + $amount: Decimal! + $memo: String! + ) { + sendCoins( + recipientCommunityIdentifier: $recipientCommunityIdentifier + recipientIdentifier: $recipientIdentifier + amount: $amount + memo: $memo + ) } ` 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 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)