From a71877abed87ea89a6a715a8ae11f73120eff029 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Fri, 15 Sep 2023 21:10:38 +0200 Subject: [PATCH] tests for all sendCoins 2-Phase-Commit handshake requests --- .../client/1_0/model/SendCoinsArgs.ts | 10 +- .../client/1_0/query/revertSendCoins.ts | 20 +- .../1_0/query/revertSettledSendCoins.ts | 22 +- .../client/1_0/query/settleSendCoins.ts | 22 +- .../client/1_0/query/voteForSendCoins.ts | 20 +- .../Transaction.ts | 5 + federation/package.json | 4 +- .../graphql/api/1_0/model/SendCoinsArgs.ts | 10 +- .../1_0/resolver/SendCoinsResolver.test.ts | 581 ++++++++++-------- .../api/1_0/resolver/SendCoinsResolver.ts | 213 +++---- .../util/revertSettledReceiveTransaction.ts | 8 +- .../util/settlePendingReceiveTransaction.ts | 20 +- .../src/graphql/util/findUserByIdentifier.ts | 42 ++ federation/src/graphql/util/validateAlias.ts | 39 ++ federation/yarn.lock | 15 +- 15 files changed, 616 insertions(+), 415 deletions(-) create mode 100644 federation/src/graphql/util/findUserByIdentifier.ts create mode 100644 federation/src/graphql/util/validateAlias.ts diff --git a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts index 545aab822..fb97da925 100644 --- a/backend/src/federation/client/1_0/model/SendCoinsArgs.ts +++ b/backend/src/federation/client/1_0/model/SendCoinsArgs.ts @@ -4,10 +4,10 @@ import { ArgsType, Field } from 'type-graphql' @ArgsType() export class SendCoinsArgs { @Field(() => String) - communityReceiverIdentifier: string + recipientCommunityUuid: string @Field(() => String) - userReceiverIdentifier: string + recipientUserIdentifier: string @Field(() => String) creationDate: string @@ -19,11 +19,11 @@ export class SendCoinsArgs { memo: string @Field(() => String) - communitySenderIdentifier: string + senderCommunityUuid: string @Field(() => String) - userSenderIdentifier: string + senderUserUuid: string @Field(() => String) - userSenderName: string + senderUserName: string } diff --git a/backend/src/federation/client/1_0/query/revertSendCoins.ts b/backend/src/federation/client/1_0/query/revertSendCoins.ts index 881107cb4..9cc23fe64 100644 --- a/backend/src/federation/client/1_0/query/revertSendCoins.ts +++ b/backend/src/federation/client/1_0/query/revertSendCoins.ts @@ -2,24 +2,24 @@ import { gql } from 'graphql-request' export const revertSendCoins = gql` mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! $creationDate: String! $amount: Decimal! $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! ) { revertSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier creationDate: $creationDate amount: $amount memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + 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 0d4447507..74cdbd867 100644 --- a/backend/src/federation/client/1_0/query/revertSettledSendCoins.ts +++ b/backend/src/federation/client/1_0/query/revertSettledSendCoins.ts @@ -2,24 +2,24 @@ import { gql } from 'graphql-request' export const revertSettledSendCoins = gql` mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! - $creationDate: Date! + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! + $creationDate: String! $amount: Decimal! $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! ) { revertSettledSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier creationDate: $creationDate amount: $amount memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + 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 99f784bc7..1696a0900 100644 --- a/backend/src/federation/client/1_0/query/settleSendCoins.ts +++ b/backend/src/federation/client/1_0/query/settleSendCoins.ts @@ -2,24 +2,24 @@ import { gql } from 'graphql-request' export const settleSendCoins = gql` mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! - $creationDate: Date! + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! + $creationDate: String! $amount: Decimal! $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! ) { settleSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier creationDate: $creationDate amount: $amount memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + senderUserName: $senderUserName ) } ` diff --git a/backend/src/federation/client/1_0/query/voteForSendCoins.ts b/backend/src/federation/client/1_0/query/voteForSendCoins.ts index f0f75198f..0f16ff32b 100644 --- a/backend/src/federation/client/1_0/query/voteForSendCoins.ts +++ b/backend/src/federation/client/1_0/query/voteForSendCoins.ts @@ -2,24 +2,24 @@ import { gql } from 'graphql-request' export const voteForSendCoins = gql` mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! $creationDate: String! $amount: Decimal! $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! ) { voteForSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier creationDate: $creationDate amount: $amount memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + senderUserName: $senderUserName ) } ` diff --git a/database/entity/0072-add_communityuuid_to_transactions_table/Transaction.ts b/database/entity/0072-add_communityuuid_to_transactions_table/Transaction.ts index 225ef9d23..3efa78ada 100644 --- a/database/entity/0072-add_communityuuid_to_transactions_table/Transaction.ts +++ b/database/entity/0072-add_communityuuid_to_transactions_table/Transaction.ts @@ -3,6 +3,7 @@ import { Decimal } from 'decimal.js-light' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { Contribution } from '../Contribution' +import { DltTransaction } from '../DltTransaction' @Entity('transactions') export class Transaction extends BaseEntity { @@ -152,6 +153,10 @@ export class Transaction extends BaseEntity { @JoinColumn({ name: 'id', referencedColumnName: 'transactionId' }) contribution?: Contribution | null + @OneToOne(() => DltTransaction, (dlt) => dlt.transactionId) + @JoinColumn({ name: 'id', referencedColumnName: 'transactionId' }) + dltTransaction?: DltTransaction | null + @OneToOne(() => Transaction) @JoinColumn({ name: 'previous' }) previousTransaction?: Transaction | null diff --git a/federation/package.json b/federation/package.json index eb78d6be0..aaeaff08d 100644 --- a/federation/package.json +++ b/federation/package.json @@ -16,6 +16,7 @@ "lint": "eslint --max-warnings=0 --ext .js,.ts ." }, "dependencies": { + "@types/uuid": "8.3.4", "apollo-server-express": "^2.25.2", "await-semaphore": "0.1.3", "class-validator": "^0.13.2", @@ -28,7 +29,8 @@ "lodash.clonedeep": "^4.5.0", "log4js": "^6.7.1", "reflect-metadata": "^0.1.13", - "type-graphql": "^1.1.1" + "type-graphql": "^1.1.1", + "uuid": "8.3.2" }, "devDependencies": { "@types/express": "4.17.12", diff --git a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts index 545aab822..fb97da925 100644 --- a/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts +++ b/federation/src/graphql/api/1_0/model/SendCoinsArgs.ts @@ -4,10 +4,10 @@ import { ArgsType, Field } from 'type-graphql' @ArgsType() export class SendCoinsArgs { @Field(() => String) - communityReceiverIdentifier: string + recipientCommunityUuid: string @Field(() => String) - userReceiverIdentifier: string + recipientUserIdentifier: string @Field(() => String) creationDate: string @@ -19,11 +19,11 @@ export class SendCoinsArgs { memo: string @Field(() => String) - communitySenderIdentifier: string + senderCommunityUuid: string @Field(() => String) - userSenderIdentifier: string + senderUserUuid: string @Field(() => String) - userSenderName: string + senderUserName: string } 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 ce01a0f85..5120391d6 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -4,6 +4,7 @@ import { ApolloServerTestClient } from 'apollo-server-testing' import { Community as DbCommunity } from '@entity/Community' import CONFIG from '@/config' import { User as DbUser } from '@entity/User' +import { UserContact as DbUserContact } from '@entity/UserContact' import { fullName } from '@/graphql/util/fullName' import { GraphQLError } from 'graphql' import { cleanDB, testEnvironment } from '@test/helpers' @@ -11,9 +12,9 @@ import { logger } from '@test/testSetup' import { Connection } from '@dbTools/typeorm' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' import Decimal from 'decimal.js-light' -import { calculateRecipientBalance } from '../util/calculateRecipientBalance' import { PendingTransactionState } from '../enum/PendingTransactionState' import { TransactionTypeId } from '../enum/TransactionTypeId' +import { Transaction as DbTransaction } from '@entity/Transaction' let mutate: ApolloServerTestClient['mutate'], con: Connection // let query: ApolloServerTestClient['query'] @@ -26,18 +27,18 @@ let testEnv: { CONFIG.FEDERATION_API = '1_0' +let homeCom: DbCommunity +let foreignCom: DbCommunity +let sendUser: DbUser +let sendContact: DbUserContact +let recipUser: DbUser +let recipContact: DbUserContact + beforeAll(async () => { testEnv = await testEnvironment(logger) mutate = testEnv.mutate // query = testEnv.query con = testEnv.con - - // const server = await createServer() - // con = server.con - // query = createTestClient(server.apollo).query - // mutate = createTestClient(server.apollo).mutate - // DbCommunity.clear() - // DbUser.clear() await cleanDB() }) @@ -48,118 +49,103 @@ afterAll(async () => { describe('SendCoinsResolver', () => { const voteForSendCoinsMutation = ` - mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! - $creationDate: String! - $amount: Decimal! - $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! - ) { - voteForSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier - creationDate: $creationDate - amount: $amount - memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName - ) - } -` - const revertSendCoinsMutation = ` - mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! - $creationDate: String! - $amount: Decimal! - $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! - ) { - revertSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier - creationDate: $creationDate - amount: $amount - memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName - ) - } -` + mutation ( + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! + $creationDate: String! + $amount: Decimal! + $memo: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! + ) { + voteForSendCoins( + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier + creationDate: $creationDate + amount: $amount + memo: $memo + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + senderUserName: $senderUserName + ) + }` const settleSendCoinsMutation = ` mutation ( - $communityReceiverIdentifier: String! - $userReceiverIdentifier: String! + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! $creationDate: String! $amount: Decimal! $memo: String! - $communitySenderIdentifier: String! - $userSenderIdentifier: String! - $userSenderName: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! ) { settleSendCoins( - communityReceiverIdentifier: $communityReceiverIdentifier - userReceiverIdentifier: $userReceiverIdentifier + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier creationDate: $creationDate amount: $amount memo: $memo - communitySenderIdentifier: $communitySenderIdentifier - userSenderIdentifier: $userSenderIdentifier - userSenderName: $userSenderName + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + senderUserName: $senderUserName ) - } -` + }` + + beforeEach(async () => { + await cleanDB() + homeCom = DbCommunity.create() + homeCom.foreign = false + homeCom.url = 'homeCom-url' + homeCom.name = 'homeCom-Name' + homeCom.description = 'homeCom-Description' + homeCom.creationDate = new Date() + homeCom.publicKey = Buffer.from('homeCom-publicKey') + homeCom.communityUuid = '56a55482-909e-46a4-bfa2-cd025e894eba' + await DbCommunity.insert(homeCom) + + foreignCom = DbCommunity.create() + foreignCom.foreign = true + foreignCom.url = 'foreignCom-url' + foreignCom.name = 'foreignCom-Name' + foreignCom.description = 'foreignCom-Description' + foreignCom.creationDate = new Date() + foreignCom.publicKey = Buffer.from('foreignCom-publicKey') + foreignCom.communityUuid = '56a55482-909e-46a4-bfa2-cd025e894ebb' + await DbCommunity.insert(foreignCom) + + sendUser = DbUser.create() + sendUser.alias = 'sendUser-alias' + sendUser.firstName = 'sendUser-FirstName' + sendUser.gradidoID = '56a55482-909e-46a4-bfa2-cd025e894ebc' + sendUser.lastName = 'sendUser-LastName' + await DbUser.insert(sendUser) + + sendContact = await newEmailContact('send.user@email.de', sendUser.id) + sendContact = await DbUserContact.save(sendContact) + + sendUser.emailContact = sendContact + sendUser.emailId = sendContact.id + await DbUser.save(sendUser) + + recipUser = DbUser.create() + recipUser.alias = 'recipUser-alias' + recipUser.firstName = 'recipUser-FirstName' + recipUser.gradidoID = '56a55482-909e-46a4-bfa2-cd025e894ebd' + recipUser.lastName = 'recipUser-LastName' + await DbUser.insert(recipUser) + + recipContact = await newEmailContact('recip.user@email.de', recipUser.id) + recipContact = await DbUserContact.save(recipContact) + + recipUser.emailContact = recipContact + recipUser.emailId = recipContact.id + await DbUser.save(recipUser) + }) + describe('voteForSendCoins', () => { - let homeCom: DbCommunity - let foreignCom: DbCommunity - let sendUser: DbUser - let recipUser: DbUser - - beforeEach(async () => { - await cleanDB() - homeCom = DbCommunity.create() - homeCom.foreign = false - homeCom.url = 'homeCom-url' - homeCom.name = 'homeCom-Name' - homeCom.description = 'homeCom-Description' - homeCom.creationDate = new Date() - homeCom.publicKey = Buffer.from('homeCom-publicKey') - homeCom.communityUuid = 'homeCom-UUID' - await DbCommunity.insert(homeCom) - - foreignCom = DbCommunity.create() - foreignCom.foreign = true - foreignCom.url = 'foreignCom-url' - foreignCom.name = 'foreignCom-Name' - foreignCom.description = 'foreignCom-Description' - foreignCom.creationDate = new Date() - foreignCom.publicKey = Buffer.from('foreignCom-publicKey') - foreignCom.communityUuid = 'foreignCom-UUID' - await DbCommunity.insert(foreignCom) - - sendUser = DbUser.create() - sendUser.alias = 'sendUser-alias' - sendUser.firstName = 'sendUser-FirstName' - sendUser.gradidoID = 'sendUser-GradidoID' - sendUser.lastName = 'sendUser-LastName' - await DbUser.insert(sendUser) - - recipUser = DbUser.create() - recipUser.alias = 'recipUser-alias' - recipUser.firstName = 'recipUser-FirstName' - recipUser.gradidoID = 'recipUser-GradidoID' - recipUser.lastName = 'recipUser-LastName' - await DbUser.insert(recipUser) - }) - describe('unknown recipient community', () => { it('throws an error', async () => { jest.clearAllMocks() @@ -167,19 +153,19 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: voteForSendCoinsMutation, variables: { - communityReceiverIdentifier: 'invalid foreignCom', - userReceiverIdentifier: recipUser.gradidoID, + recipientCommunityUuid: 'invalid foreignCom', + recipientUserIdentifier: recipUser.gradidoID, creationDate: new Date().toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: homeCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: homeCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('voteForSendCoins with wrong communityReceiverIdentifier')], + errors: [new GraphQLError('voteForSendCoins with wrong recipientCommunityUuid')], }), ) }) @@ -192,21 +178,21 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: voteForSendCoinsMutation, variables: { - communityReceiverIdentifier: foreignCom.communityUuid, - userReceiverIdentifier: 'invalid recipient', + recipientCommunityUuid: foreignCom.communityUuid, + recipientUserIdentifier: 'invalid recipient', creationDate: new Date().toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: homeCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: homeCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( expect.objectContaining({ errors: [ new GraphQLError( - 'voteForSendCoins with unknown userReceiverIdentifier in the community=', + 'voteForSendCoins with unknown recipientUserIdentifier in the community=', ), ], }), @@ -221,14 +207,14 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: voteForSendCoinsMutation, variables: { - communityReceiverIdentifier: foreignCom.communityUuid, - userReceiverIdentifier: recipUser.gradidoID, + recipientCommunityUuid: foreignCom.communityUuid, + recipientUserIdentifier: recipUser.gradidoID, creationDate: new Date().toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: homeCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: homeCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( @@ -243,59 +229,43 @@ describe('SendCoinsResolver', () => { }) describe('revertSendCoins', () => { - let homeCom: DbCommunity - let foreignCom: DbCommunity - let sendUser: DbUser - let recipUser: DbUser + const revertSendCoinsMutation = ` + mutation ( + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! + $creationDate: String! + $amount: Decimal! + $memo: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! + ) { + revertSendCoins( + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier + creationDate: $creationDate + amount: $amount + memo: $memo + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + senderUserName: $senderUserName + ) + }` + const creationDate = new Date() beforeEach(async () => { - await cleanDB() - homeCom = DbCommunity.create() - homeCom.foreign = false - homeCom.url = 'homeCom-url' - homeCom.name = 'homeCom-Name' - homeCom.description = 'homeCom-Description' - homeCom.creationDate = new Date() - homeCom.publicKey = Buffer.from('homeCom-publicKey') - homeCom.communityUuid = 'homeCom-UUID' - await DbCommunity.insert(homeCom) - - foreignCom = DbCommunity.create() - foreignCom.foreign = true - foreignCom.url = 'foreignCom-url' - foreignCom.name = 'foreignCom-Name' - foreignCom.description = 'foreignCom-Description' - foreignCom.creationDate = new Date() - foreignCom.publicKey = Buffer.from('foreignCom-publicKey') - foreignCom.communityUuid = 'foreignCom-UUID' - await DbCommunity.insert(foreignCom) - - sendUser = DbUser.create() - sendUser.alias = 'sendUser-alias' - sendUser.firstName = 'sendUser-FirstName' - sendUser.gradidoID = 'sendUser-GradidoID' - sendUser.lastName = 'sendUser-LastName' - await DbUser.insert(sendUser) - - recipUser = DbUser.create() - recipUser.alias = 'recipUser-alias' - recipUser.firstName = 'recipUser-FirstName' - recipUser.gradidoID = 'recipUser-GradidoID' - recipUser.lastName = 'recipUser-LastName' - await DbUser.insert(recipUser) - await mutate({ mutation: voteForSendCoinsMutation, variables: { - communityReceiverIdentifier: foreignCom.communityUuid, - userReceiverIdentifier: recipUser.gradidoID, + recipientCommunityUuid: foreignCom.communityUuid, + recipientUserIdentifier: recipUser.gradidoID, creationDate: creationDate.toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: homeCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: homeCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }) }) @@ -307,19 +277,19 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: revertSendCoinsMutation, variables: { - communityReceiverIdentifier: 'invalid foreignCom', - userReceiverIdentifier: recipUser.gradidoID, + recipientCommunityUuid: 'invalid foreignCom', + recipientUserIdentifier: recipUser.gradidoID, creationDate: creationDate.toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: homeCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: homeCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('revertSendCoins with wrong communityReceiverIdentifier')], + errors: [new GraphQLError('revertSendCoins with wrong recipientCommunityUuid')], }), ) }) @@ -332,21 +302,21 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: revertSendCoinsMutation, variables: { - communityReceiverIdentifier: foreignCom.communityUuid, - userReceiverIdentifier: 'invalid recipient', + recipientCommunityUuid: foreignCom.communityUuid, + recipientUserIdentifier: 'invalid recipient', creationDate: creationDate.toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: homeCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: homeCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( expect.objectContaining({ errors: [ new GraphQLError( - 'revertSendCoins with unknown userReceiverIdentifier in the community=', + 'revertSendCoins with unknown recipientUserIdentifier in the community=', ), ], }), @@ -361,14 +331,14 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: revertSendCoinsMutation, variables: { - communityReceiverIdentifier: foreignCom.communityUuid, - userReceiverIdentifier: recipUser.gradidoID, + recipientCommunityUuid: foreignCom.communityUuid, + recipientUserIdentifier: recipUser.gradidoID, creationDate: creationDate.toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: homeCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: homeCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( @@ -383,61 +353,26 @@ describe('SendCoinsResolver', () => { }) describe('settleSendCoins', () => { - let homeCom: DbCommunity - let foreignCom: DbCommunity - let sendUser: DbUser - let recipUser: DbUser let pendingTx: DbPendingTransaction const creationDate = new Date() beforeEach(async () => { - await cleanDB() - homeCom = DbCommunity.create() - homeCom.foreign = false - homeCom.url = 'homeCom-url' - homeCom.name = 'homeCom-Name' - homeCom.description = 'homeCom-Description' - homeCom.creationDate = new Date() - homeCom.publicKey = Buffer.from('homeCom-publicKey') - homeCom.communityUuid = 'homeCom-UUID' - await DbCommunity.insert(homeCom) - - foreignCom = DbCommunity.create() - foreignCom.foreign = true - foreignCom.url = 'foreignCom-url' - foreignCom.name = 'foreignCom-Name' - foreignCom.description = 'foreignCom-Description' - foreignCom.creationDate = new Date() - foreignCom.publicKey = Buffer.from('foreignCom-publicKey') - foreignCom.communityUuid = 'foreignCom-UUID' - await DbCommunity.insert(foreignCom) - - sendUser = DbUser.create() - sendUser.alias = 'sendUser-alias' - sendUser.firstName = 'sendUser-FirstName' - sendUser.gradidoID = 'sendUser-GradidoID' - sendUser.lastName = 'sendUser-LastName' - await DbUser.insert(sendUser) - - recipUser = DbUser.create() - recipUser.alias = 'recipUser-alias' - recipUser.firstName = 'recipUser-FirstName' - recipUser.gradidoID = 'recipUser-GradidoID' - recipUser.lastName = 'recipUser-LastName' - await DbUser.insert(recipUser) - pendingTx = DbPendingTransaction.create() pendingTx.amount = new Decimal(100) pendingTx.balanceDate = creationDate // pendingTx.balance = new Decimal(0) pendingTx.linkedUserId = sendUser.id - pendingTx.linkedUserCommunityUuid = foreignCom.communityUuid + if (foreignCom.communityUuid) { + pendingTx.linkedUserCommunityUuid = foreignCom.communityUuid + } pendingTx.linkedUserGradidoID = sendUser.gradidoID pendingTx.state = PendingTransactionState.NEW pendingTx.typeId = TransactionTypeId.RECEIVE pendingTx.memo = 'X-Com-TX memo' pendingTx.userId = recipUser.id - pendingTx.userCommunityUuid = homeCom.communityUuid + if (homeCom.communityUuid) { + pendingTx.userCommunityUuid = homeCom.communityUuid + } pendingTx.userGradidoID = recipUser.gradidoID await DbPendingTransaction.insert(pendingTx) }) @@ -449,19 +384,19 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: settleSendCoinsMutation, variables: { - communityReceiverIdentifier: 'invalid foreignCom', - userReceiverIdentifier: recipUser.gradidoID, + recipientCommunityUuid: 'invalid foreignCom', + recipientUserIdentifier: recipUser.gradidoID, creationDate: creationDate.toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: foreignCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: foreignCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('settleSendCoins with wrong communityReceiverIdentifier')], + errors: [new GraphQLError('settleSendCoins with wrong recipientCommunityUuid')], }), ) }) @@ -474,21 +409,21 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: settleSendCoinsMutation, variables: { - communityReceiverIdentifier: homeCom.communityUuid, - userReceiverIdentifier: 'invalid recipient', + recipientCommunityUuid: homeCom.communityUuid, + recipientUserIdentifier: 'invalid recipient', creationDate: creationDate.toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: foreignCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: foreignCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( expect.objectContaining({ errors: [ new GraphQLError( - 'settleSendCoins with unknown userReceiverIdentifier in the community=', + 'settleSendCoins with unknown recipientUserIdentifier in the community=', ), ], }), @@ -503,14 +438,14 @@ describe('SendCoinsResolver', () => { await mutate({ mutation: settleSendCoinsMutation, variables: { - communityReceiverIdentifier: homeCom.communityUuid, - userReceiverIdentifier: recipUser.gradidoID, + recipientCommunityUuid: homeCom.communityUuid, + recipientUserIdentifier: recipUser.gradidoID, creationDate: creationDate.toISOString(), amount: 100, memo: 'X-Com-TX memo', - communitySenderIdentifier: foreignCom.communityUuid, - userSenderIdentifier: sendUser.gradidoID, - userSenderName: fullName(sendUser.firstName, sendUser.lastName), + senderCommunityUuid: foreignCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), }, }), ).toEqual( @@ -523,4 +458,164 @@ describe('SendCoinsResolver', () => { }) }) }) + + describe('revertSettledSendCoins', () => { + const revertSettledSendCoinsMutation = ` + mutation ( + $recipientCommunityUuid: String! + $recipientUserIdentifier: String! + $creationDate: String! + $amount: Decimal! + $memo: String! + $senderCommunityUuid: String! + $senderUserUuid: String! + $senderUserName: String! + ) { + revertSettledSendCoins( + recipientCommunityUuid: $recipientCommunityUuid + recipientUserIdentifier: $recipientUserIdentifier + creationDate: $creationDate + amount: $amount + memo: $memo + senderCommunityUuid: $senderCommunityUuid + senderUserUuid: $senderUserUuid + senderUserName: $senderUserName + ) + }` + + let pendingTx: DbPendingTransaction + let settledTx: DbTransaction + const creationDate = new Date() + + beforeEach(async () => { + pendingTx = DbPendingTransaction.create() + pendingTx.amount = new Decimal(100) + pendingTx.balanceDate = creationDate + // pendingTx.balance = new Decimal(0) + pendingTx.linkedUserId = sendUser.id + if (foreignCom.communityUuid) { + pendingTx.linkedUserCommunityUuid = foreignCom.communityUuid + } + pendingTx.linkedUserGradidoID = sendUser.gradidoID + pendingTx.linkedUserName = fullName(sendUser.firstName, sendUser.lastName) + pendingTx.state = PendingTransactionState.SETTLED + pendingTx.typeId = TransactionTypeId.RECEIVE + pendingTx.memo = 'X-Com-TX memo' + pendingTx.userId = recipUser.id + if (homeCom.communityUuid) { + pendingTx.userCommunityUuid = homeCom.communityUuid + } + pendingTx.userGradidoID = recipUser.gradidoID + await DbPendingTransaction.insert(pendingTx) + + settledTx = DbTransaction.create() + settledTx.amount = new Decimal(100) + settledTx.balanceDate = creationDate + // pendingTx.balance = new Decimal(0) + settledTx.linkedUserId = sendUser.id + settledTx.linkedUserCommunityUuid = foreignCom.communityUuid + settledTx.linkedUserGradidoID = sendUser.gradidoID + settledTx.linkedUserName = fullName(sendUser.firstName, sendUser.lastName) + settledTx.typeId = TransactionTypeId.RECEIVE + settledTx.memo = 'X-Com-TX memo' + settledTx.userId = recipUser.id + if (homeCom.communityUuid) { + settledTx.userCommunityUuid = homeCom.communityUuid + } + settledTx.userGradidoID = recipUser.gradidoID + await DbTransaction.insert(settledTx) + }) + + describe('unknown recipient community', () => { + it('throws an error', async () => { + jest.clearAllMocks() + expect( + await mutate({ + mutation: revertSettledSendCoinsMutation, + variables: { + recipientCommunityUuid: 'invalid foreignCom', + recipientUserIdentifier: recipUser.gradidoID, + creationDate: creationDate.toISOString(), + amount: 100, + memo: 'X-Com-TX memo', + senderCommunityUuid: foreignCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), + }, + }), + ).toEqual( + expect.objectContaining({ + errors: [new GraphQLError('revertSettledSendCoins with wrong recipientCommunityUuid')], + }), + ) + }) + }) + + describe('unknown recipient user', () => { + it('throws an error', async () => { + jest.clearAllMocks() + expect( + await mutate({ + mutation: revertSettledSendCoinsMutation, + variables: { + recipientCommunityUuid: homeCom.communityUuid, + recipientUserIdentifier: 'invalid recipient', + creationDate: creationDate.toISOString(), + amount: 100, + memo: 'X-Com-TX memo', + senderCommunityUuid: foreignCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), + }, + }), + ).toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'revertSettledSendCoins with unknown recipientUserIdentifier in the community=', + ), + ], + }), + ) + }) + }) + + describe('valid X-Com-TX settled', () => { + it('throws an error', async () => { + jest.clearAllMocks() + expect( + await mutate({ + mutation: revertSettledSendCoinsMutation, + variables: { + recipientCommunityUuid: homeCom.communityUuid, + recipientUserIdentifier: recipUser.gradidoID, + creationDate: creationDate.toISOString(), + amount: 100, + memo: 'X-Com-TX memo', + senderCommunityUuid: foreignCom.communityUuid, + senderUserUuid: sendUser.gradidoID, + senderUserName: fullName(sendUser.firstName, sendUser.lastName), + }, + }), + ).toEqual( + expect.objectContaining({ + data: { + revertSettledSendCoins: true, + }, + }), + ) + }) + }) + }) }) + +async function newEmailContact(email: string, userId: number): Promise { + const emailContact = new DbUserContact() + emailContact.email = email + emailContact.userId = userId + emailContact.type = 'EMAIL' + emailContact.emailChecked = false + emailContact.emailOptInTypeId = 1 + emailContact.emailVerificationCode = '1' + userId + return emailContact +} diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts index 39c6f1e95..4196e015c 100644 --- a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.ts @@ -4,7 +4,6 @@ import { federationLogger as logger } from '@/server/logger' import { Community as DbCommunity } from '@entity/Community' import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTransaction' import { SendCoinsArgs } from '../model/SendCoinsArgs' -import { User as DbUser } from '@entity/User' import { LogError } from '@/server/LogError' import { PendingTransactionState } from '../enum/PendingTransactionState' import { TransactionTypeId } from '../enum/TransactionTypeId' @@ -14,6 +13,7 @@ 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' @Resolver() // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -22,33 +22,46 @@ export class SendCoinsResolver { async voteForSendCoins( @Args() { - communityReceiverIdentifier, - userReceiverIdentifier, + recipientCommunityUuid, + recipientUserIdentifier, creationDate, amount, memo, - communitySenderIdentifier, - userSenderIdentifier, - userSenderName, + senderCommunityUuid, + senderUserUuid, + senderUserName, }: SendCoinsArgs, - ): Promise { - logger.debug(`voteForSendCoins() via apiVersion=1_0 ...`) - let result: string | null = null + ): Promise { + logger.debug( + `voteForSendCoins() via apiVersion=1_0 ...`, + recipientCommunityUuid, + recipientUserIdentifier, + creationDate, + amount.toString(), + memo, + senderCommunityUuid, + senderUserUuid, + senderUserName, + ) + let result: string // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: communityReceiverIdentifier, + communityUuid: recipientCommunityUuid, }) if (!homeCom) { throw new LogError( - `voteForSendCoins with wrong communityReceiverIdentifier`, - communityReceiverIdentifier, + `voteForSendCoins with wrong recipientCommunityUuid`, + recipientCommunityUuid, ) } - // second check if receiver user exists in this community - const receiverUser = await DbUser.findOneBy({ gradidoID: userReceiverIdentifier }) - if (!receiverUser) { + let receiverUser + try { + // second check if receiver user exists in this community + receiverUser = await findUserByIdentifier(recipientUserIdentifier) + } catch (err) { + logger.error('Error in findUserByIdentifier:', err) throw new LogError( - `voteForSendCoins with unknown userReceiverIdentifier in the community=`, + `voteForSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, ) } @@ -57,21 +70,21 @@ export class SendCoinsResolver { const receiveBalance = await calculateRecipientBalance(receiverUser.id, amount, txDate) const pendingTx = DbPendingTransaction.create() pendingTx.amount = amount - pendingTx.balance = receiveBalance ? receiveBalance.balance : new Decimal(0) + pendingTx.balance = receiveBalance ? receiveBalance.balance : 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 = communitySenderIdentifier - pendingTx.linkedUserGradidoID = userSenderIdentifier - pendingTx.linkedUserName = userSenderName + pendingTx.linkedUserCommunityUuid = senderCommunityUuid + pendingTx.linkedUserGradidoID = senderUserUuid + pendingTx.linkedUserName = senderUserName pendingTx.memo = memo pendingTx.previous = receiveBalance ? receiveBalance.lastTransactionId : null pendingTx.state = PendingTransactionState.NEW pendingTx.typeId = TransactionTypeId.RECEIVE pendingTx.userId = receiverUser.id - pendingTx.userCommunityUuid = communityReceiverIdentifier - pendingTx.userGradidoID = userReceiverIdentifier + pendingTx.userCommunityUuid = recipientCommunityUuid + pendingTx.userGradidoID = receiverUser.gradidoID pendingTx.userName = fullName(receiverUser.firstName, receiverUser.lastName) await DbPendingTransaction.insert(pendingTx) @@ -87,44 +100,47 @@ export class SendCoinsResolver { async revertSendCoins( @Args() { - communityReceiverIdentifier, - userReceiverIdentifier, + recipientCommunityUuid, + recipientUserIdentifier, creationDate, amount, memo, - communitySenderIdentifier, - userSenderIdentifier, - userSenderName, + senderCommunityUuid, + senderUserUuid, + senderUserName, }: SendCoinsArgs, ): Promise { logger.debug(`revertSendCoins() via apiVersion=1_0 ...`) // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: communityReceiverIdentifier, + communityUuid: recipientCommunityUuid, }) if (!homeCom) { throw new LogError( - `revertSendCoins with wrong communityReceiverIdentifier`, - communityReceiverIdentifier, + `revertSendCoins with wrong recipientCommunityUuid`, + recipientCommunityUuid, ) } - // second check if receiver user exists in this community - const receiverUser = await DbUser.findOneBy({ gradidoID: userReceiverIdentifier }) - if (!receiverUser) { + let receiverUser + try { + // second check if receiver user exists in this community + receiverUser = await findUserByIdentifier(recipientUserIdentifier) + } catch (err) { + logger.error('Error in findUserByIdentifier:', err) throw new LogError( - `revertSendCoins with unknown userReceiverIdentifier in the community=`, + `revertSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, ) } try { const pendingTx = await DbPendingTransaction.findOneBy({ - userCommunityUuid: communityReceiverIdentifier, - userGradidoID: userReceiverIdentifier, + userCommunityUuid: recipientCommunityUuid, + userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW, typeId: TransactionTypeId.RECEIVE, balanceDate: new Date(creationDate), - linkedUserCommunityUuid: communitySenderIdentifier, - linkedUserGradidoID: userSenderIdentifier, + linkedUserCommunityUuid: senderCommunityUuid, + linkedUserGradidoID: senderUserUuid, }) logger.debug('XCom: revertSendCoins found pendingTX=', pendingTx) if (pendingTx && pendingTx.amount.toString() === amount.toString()) { @@ -143,16 +159,16 @@ export class SendCoinsResolver { ) throw new LogError( `Can't find in revertSendCoins the pending receiver TX for args=`, - communityReceiverIdentifier, - userReceiverIdentifier, + recipientCommunityUuid, + recipientUserIdentifier, PendingTransactionState.NEW, TransactionTypeId.RECEIVE, creationDate, amount, memo, - communitySenderIdentifier, - userSenderIdentifier, - userSenderName, + senderCommunityUuid, + senderUserUuid, + senderUserName, ) } logger.debug(`revertSendCoins()-1_0... successfull`) @@ -166,56 +182,53 @@ export class SendCoinsResolver { async settleSendCoins( @Args() { - communityReceiverIdentifier, - userReceiverIdentifier, + recipientCommunityUuid, + recipientUserIdentifier, creationDate, amount, memo, - communitySenderIdentifier, - userSenderIdentifier, - userSenderName, + senderCommunityUuid, + senderUserUuid, + senderUserName, }: SendCoinsArgs, ): Promise { logger.debug( - `settleSendCoins() via apiVersion=1_0 ...userCommunityUuid=${communityReceiverIdentifier}, userGradidoID=${userReceiverIdentifier}, balanceDate=${creationDate},amount=${amount.valueOf()}, memo=${memo}, linkedUserCommunityUuid = ${communitySenderIdentifier}, userSenderIdentifier=${userSenderIdentifier}, userSenderName=${userSenderName}`, + `settleSendCoins() via apiVersion=1_0 ...userCommunityUuid=${recipientCommunityUuid}, userGradidoID=${recipientUserIdentifier}, balanceDate=${creationDate},amount=${amount.valueOf()}, memo=${memo}, linkedUserCommunityUuid = ${senderCommunityUuid}, userSenderIdentifier=${senderUserUuid}, userSenderName=${senderUserName}`, ) // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: communityReceiverIdentifier, + communityUuid: recipientCommunityUuid, }) if (!homeCom) { throw new LogError( - `settleSendCoins with wrong communityReceiverIdentifier`, - communityReceiverIdentifier, + `settleSendCoins with wrong recipientCommunityUuid`, + recipientCommunityUuid, ) } - // second check if receiver user exists in this community - const receiverUser = await DbUser.findOneBy({ gradidoID: userReceiverIdentifier }) - if (!receiverUser) { + let receiverUser + try { + // second check if receiver user exists in this community + receiverUser = await findUserByIdentifier(recipientUserIdentifier) + } catch (err) { + logger.error('Error in findUserByIdentifier:', err) throw new LogError( - `settleSendCoins with unknown userReceiverIdentifier in the community=`, + `settleSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, ) } - // try { const pendingTx = await DbPendingTransaction.findOneBy({ - userCommunityUuid: communityReceiverIdentifier, - userGradidoID: userReceiverIdentifier, + userCommunityUuid: recipientCommunityUuid, + userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.NEW, typeId: TransactionTypeId.RECEIVE, balanceDate: new Date(creationDate), - linkedUserCommunityUuid: communitySenderIdentifier, - linkedUserGradidoID: userSenderIdentifier, + linkedUserCommunityUuid: senderCommunityUuid, + linkedUserGradidoID: senderUserUuid, }) logger.debug('XCom: settleSendCoins found pendingTX=', pendingTx?.toString()) if (pendingTx && pendingTx.amount.toString() === amount.toString() && pendingTx.memo === memo) { logger.debug('XCom: settleSendCoins matching pendingTX for settlement...') - const homeCom = await DbCommunity.findOneByOrFail({ - communityUuid: communityReceiverIdentifier, - }) - const receiverUser = await DbUser.findOneByOrFail({ gradidoID: userReceiverIdentifier }) - await settlePendingReceiveTransaction(homeCom, receiverUser, pendingTx) logger.debug(`XCom: settlePendingReceiveTransaction()-1_0... successfull`) return true @@ -223,70 +236,67 @@ 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=`, - communityReceiverIdentifier, - userReceiverIdentifier, + recipientCommunityUuid, + recipientUserIdentifier, PendingTransactionState.NEW, TransactionTypeId.RECEIVE, creationDate, amount, memo, - communitySenderIdentifier, - userSenderIdentifier, - userSenderName, + senderCommunityUuid, + senderUserUuid, + senderUserName, ) } -/* - } catch (err) { - throw new LogError(`Error in settlePendingReceiveTransaction: `, err) - } -*/ } @Mutation(() => Boolean) async revertSettledSendCoins( @Args() { - communityReceiverIdentifier, - userReceiverIdentifier, + recipientCommunityUuid, + recipientUserIdentifier, creationDate, amount, memo, - communitySenderIdentifier, - userSenderIdentifier, - userSenderName, + senderCommunityUuid, + senderUserUuid, + senderUserName, }: SendCoinsArgs, ): Promise { - // try { logger.debug(`revertSettledSendCoins() via apiVersion=1_0 ...`) // first check if receiver community is correct const homeCom = await DbCommunity.findOneBy({ - communityUuid: communityReceiverIdentifier, + communityUuid: recipientCommunityUuid, }) if (!homeCom) { throw new LogError( - `revertSettledSendCoins with wrong communityReceiverIdentifier`, - communityReceiverIdentifier, + `revertSettledSendCoins with wrong recipientCommunityUuid`, + recipientCommunityUuid, ) } - // second check if receiver user exists in this community - const receiverUser = await DbUser.findOneBy({ gradidoID: userReceiverIdentifier }) - if (!receiverUser) { + let receiverUser + try { + // second check if receiver user exists in this community + receiverUser = await findUserByIdentifier(recipientUserIdentifier) + } catch (err) { + logger.error('Error in findUserByIdentifier:', err) throw new LogError( - `revertSettledSendCoins with unknown userReceiverIdentifier in the community=`, + `revertSettledSendCoins with unknown recipientUserIdentifier in the community=`, homeCom.name, ) } const pendingTx = await DbPendingTransaction.findOneBy({ - userCommunityUuid: communityReceiverIdentifier, - userGradidoID: userReceiverIdentifier, + userCommunityUuid: recipientCommunityUuid, + userGradidoID: receiverUser.gradidoID, state: PendingTransactionState.SETTLED, typeId: TransactionTypeId.RECEIVE, balanceDate: new Date(creationDate), - linkedUserCommunityUuid: communitySenderIdentifier, - linkedUserGradidoID: userSenderIdentifier, + linkedUserCommunityUuid: senderCommunityUuid, + linkedUserGradidoID: senderUserUuid, }) logger.debug('XCom: revertSettledSendCoins found pendingTX=', pendingTx) - if (pendingTx && pendingTx.amount === amount && pendingTx.memo === memo) { + if (pendingTx && pendingTx.amount.toString() === amount.toString() && pendingTx.memo === memo) { logger.debug('XCom: revertSettledSendCoins matching pendingTX for remove...') try { await revertSettledReceiveTransaction(homeCom, receiverUser, pendingTx) @@ -298,24 +308,19 @@ export class SendCoinsResolver { logger.debug('XCom: revertSettledSendCoins NOT matching pendingTX...') throw new LogError( `Can't find in revertSettledSendCoins the pending receiver TX for args=`, - communityReceiverIdentifier, - userReceiverIdentifier, + recipientCommunityUuid, + recipientUserIdentifier, PendingTransactionState.SETTLED, TransactionTypeId.RECEIVE, creationDate, amount, memo, - communitySenderIdentifier, - userSenderIdentifier, - userSenderName, + senderCommunityUuid, + senderUserUuid, + senderUserName, ) } logger.debug(`revertSendCoins()-1_0... successfull`) return true -/* - } catch (err) { - throw new LogError(`Error in revertSendCoins: `, err) - } -*/ } } diff --git a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts index ecf615667..6eb933f6a 100644 --- a/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/revertSettledReceiveTransaction.ts @@ -64,10 +64,10 @@ export async function revertSettledReceiveTransaction( if ( lastTransaction && lastTransaction.balance === pendingTx.balance && - lastTransaction.balanceDate === pendingTx.balanceDate && + lastTransaction.balanceDate.toISOString() === pendingTx.balanceDate.toISOString() && lastTransaction.userGradidoID === pendingTx.userGradidoID && lastTransaction.userName === pendingTx.userName && - lastTransaction.amount === pendingTx.amount && + lastTransaction.amount.toString() === pendingTx.amount.toString() && lastTransaction.memo === pendingTx.memo && lastTransaction.linkedUserGradidoID === pendingTx.linkedUserGradidoID && lastTransaction.linkedUserName === pendingTx.linkedUserName @@ -83,7 +83,9 @@ export async function revertSettledReceiveTransaction( } else { // TODO: if the last TX is not equivelant to pendingTX, the transactions must be corrected in EXPERT-MODE throw new LogError( - `X-Com: missmatching transaction order for revert settlement! lastTransation=${lastTransaction} != pendingTx=${pendingTx}`, + `X-Com: missmatching transaction order for revert settlement!`, + lastTransaction, + pendingTx, ) } diff --git a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts index 586c57998..5c522061c 100644 --- a/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts +++ b/federation/src/graphql/api/1_0/util/settlePendingReceiveTransaction.ts @@ -16,6 +16,7 @@ import { federationLogger as logger } from '@/server/logger' import { getLastTransaction } from '@/graphql/util/getLastTransaction' import { TRANSACTIONS_LOCK } from '@/graphql/util/TRANSACTIONS_LOCK' import { calculateRecipientBalance } from './calculateRecipientBalance' +import Decimal from 'decimal.js-light' export async function settlePendingReceiveTransaction( homeCom: DbCommunity, @@ -52,7 +53,7 @@ export async function settlePendingReceiveTransaction( const lastTransaction = await getLastTransaction(receiverUser.id) - if (lastTransaction === undefined && lastTransaction.id !== pendingTx.previous) { + if (lastTransaction !== null && lastTransaction.id !== pendingTx.previous) { throw new LogError( `X-Com: missmatching transaction order! lastTransationId=${lastTransaction?.id} != pendingTx.previous=${pendingTx.previous}`, ) @@ -63,9 +64,11 @@ export async function settlePendingReceiveTransaction( transactionReceive.typeId = pendingTx.typeId transactionReceive.memo = pendingTx.memo transactionReceive.userId = pendingTx.userId + transactionReceive.userCommunityUuid = pendingTx.userCommunityUuid transactionReceive.userGradidoID = pendingTx.userGradidoID transactionReceive.userName = pendingTx.userName transactionReceive.linkedUserId = pendingTx.linkedUserId + transactionReceive.linkedUserCommunityUuid = pendingTx.linkedUserCommunityUuid transactionReceive.linkedUserGradidoID = pendingTx.linkedUserGradidoID transactionReceive.linkedUserName = pendingTx.linkedUserName transactionReceive.amount = pendingTx.amount @@ -74,16 +77,19 @@ export async function settlePendingReceiveTransaction( pendingTx.amount, pendingTx.balanceDate, ) - if (receiveBalance?.balance !== pendingTx.balance) { + 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}`, + `X-Com: Calculation-Error on receiver balance: receiveBalance=${receiveBalance.balance}, pendingTx.balance=${pendingTx.balance}`, ) } - transactionReceive.balance = pendingTx.balance + transactionReceive.balance = receiveBalance ? receiveBalance.balance : pendingTx.amount transactionReceive.balanceDate = pendingTx.balanceDate - transactionReceive.decay = pendingTx.decay - transactionReceive.decayStart = pendingTx.decayStart - transactionReceive.previous = pendingTx.previous + 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) logger.debug(`receive Transaction inserted: ${dbTransaction}`) diff --git a/federation/src/graphql/util/findUserByIdentifier.ts b/federation/src/graphql/util/findUserByIdentifier.ts new file mode 100644 index 000000000..96c9eb458 --- /dev/null +++ b/federation/src/graphql/util/findUserByIdentifier.ts @@ -0,0 +1,42 @@ +import { User as DbUser } from '@entity/User' +import { UserContact as DbUserContact } from '@entity/UserContact' +import { validate, version } from 'uuid' + +import { LogError } from '@/server/LogError' + +import { VALID_ALIAS_REGEX } from './validateAlias' + +export const findUserByIdentifier = async (identifier: string): Promise => { + let user: DbUser | null + if (validate(identifier) && version(identifier) === 4) { + user = await DbUser.findOne({ where: { gradidoID: identifier }, relations: ['emailContact'] }) + if (!user) { + throw new LogError('No user found to given identifier', identifier) + } + } else if (/^.{2,}@.{2,}\..{2,}$/.exec(identifier)) { + const userContact = await DbUserContact.findOne({ + where: { + email: identifier, + emailChecked: true, + }, + relations: ['user'], + }) + if (!userContact) { + throw new LogError('No user with this credentials', identifier) + } + if (!userContact.user) { + throw new LogError('No user to given contact', identifier) + } + user = userContact.user + user.emailContact = userContact + } else if (VALID_ALIAS_REGEX.exec(identifier)) { + user = await DbUser.findOne({ where: { alias: identifier }, relations: ['emailContact'] }) + if (!user) { + throw new LogError('No user found to given identifier', identifier) + } + } else { + throw new LogError('Unknown identifier type', identifier) + } + + return user +} diff --git a/federation/src/graphql/util/validateAlias.ts b/federation/src/graphql/util/validateAlias.ts new file mode 100644 index 000000000..cd2d8fe1f --- /dev/null +++ b/federation/src/graphql/util/validateAlias.ts @@ -0,0 +1,39 @@ +import { Raw } from '@dbTools/typeorm' +import { User as DbUser } from '@entity/User' + +import { LogError } from '@/server/LogError' + +export const VALID_ALIAS_REGEX = /^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/ + +const RESERVED_ALIAS = [ + 'admin', + 'email', + 'gast', + 'gdd', + 'gradido', + 'guest', + 'home', + 'root', + 'support', + 'temp', + 'tmp', + 'tmp', + 'user', + 'usr', + 'var', +] + +export const validateAlias = async (alias: string): Promise => { + if (alias.length < 3) throw new LogError('Given alias is too short', alias) + if (alias.length > 20) throw new LogError('Given alias is too long', alias) + if (!alias.match(VALID_ALIAS_REGEX)) throw new LogError('Invalid characters in alias', alias) + if (RESERVED_ALIAS.includes(alias.toLowerCase())) + throw new LogError('Alias is not allowed', alias) + const aliasInUse = await DbUser.find({ + where: { alias: Raw((a) => `LOWER(${a}) = "${alias.toLowerCase()}"`) }, + }) + if (aliasInUse.length !== 0) { + throw new LogError('Alias already in use', alias) + } + return true +} diff --git a/federation/yarn.lock b/federation/yarn.lock index 758e7e4ce..87bd7f0d4 100644 --- a/federation/yarn.lock +++ b/federation/yarn.lock @@ -1057,6 +1057,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/uuid@8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/ws@^7.0.0": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -5442,16 +5447,16 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@8.3.2, uuid@^8.0.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@^3.1.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.0.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"