From ed4e46a14a769c8c26829c8dfbb43397d3880ec7 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Fri, 5 Jan 2024 15:54:02 +0100 Subject: [PATCH] move translation logic from backend into dlt-connector, refactor --- .../src/data/proto/3_3/TransactionBody.ts | 14 +---- .../src/data/proto/TransactionBody.builder.ts | 21 +++++++ .../src/data/proto/transactionBody.logic.ts | 57 ----------------- .../src/graphql/input/TransactionDraft.ts | 4 +- .../resolver/TransactionsResolver.test.ts | 28 ++++----- .../community/HomeCommunity.role.ts | 3 +- .../transaction/AbstractTransaction.role.ts | 62 +++++++++++++++++++ .../CreateTransationRecipe.context.ts | 41 +++++++----- .../transaction/CreationTransaction.role.ts | 18 ++++++ .../transaction/ReceiveTransaction.role.ts | 21 +++++++ .../transaction/SendTransaction.role.ts | 21 +++++++ .../transaction/TransactionRecipe.role.ts | 19 ++++-- 12 files changed, 199 insertions(+), 110 deletions(-) delete mode 100644 dlt-connector/src/data/proto/transactionBody.logic.ts create mode 100644 dlt-connector/src/interactions/backendToDb/transaction/AbstractTransaction.role.ts create mode 100644 dlt-connector/src/interactions/backendToDb/transaction/CreationTransaction.role.ts create mode 100644 dlt-connector/src/interactions/backendToDb/transaction/ReceiveTransaction.role.ts create mode 100644 dlt-connector/src/interactions/backendToDb/transaction/SendTransaction.role.ts diff --git a/dlt-connector/src/data/proto/3_3/TransactionBody.ts b/dlt-connector/src/data/proto/3_3/TransactionBody.ts index 1fd58105f..c30d2a6e7 100644 --- a/dlt-connector/src/data/proto/3_3/TransactionBody.ts +++ b/dlt-connector/src/data/proto/3_3/TransactionBody.ts @@ -7,7 +7,6 @@ import { LogError } from '@/server/LogError' import { timestampToDate } from '@/utils/typeConverter' import { AbstractTransaction } from '../AbstractTransaction' -import { determineCrossGroupType, determineOtherGroup } from '../transactionBody.logic' import { CommunityRoot } from './CommunityRoot' import { PROTO_TRANSACTION_BODY_VERSION_NUMBER } from './const' @@ -25,19 +24,12 @@ import { Timestamp } from './Timestamp' export class TransactionBody extends Message { public constructor(transaction?: TransactionDraft | CommunityDraft) { if (transaction) { - let type = CrossGroupType.LOCAL - let otherGroup = '' - if (transaction instanceof TransactionDraft) { - type = determineCrossGroupType(transaction) - otherGroup = determineOtherGroup(type, transaction) - } - super({ memo: 'Not implemented yet', createdAt: new Timestamp(new Date(transaction.createdAt)), versionNumber: PROTO_TRANSACTION_BODY_VERSION_NUMBER, - type, - otherGroup, + type: CrossGroupType.LOCAL, + otherGroup: '', }) } else { super() @@ -126,14 +118,12 @@ export class TransactionBody extends Message { public getRecipientPublicKey(): Buffer | undefined { if (this.transfer) { - // this.transfer.recipient contains the publicKey of the recipient return this.transfer.recipient } if (this.creation) { return this.creation.recipient.pubkey } if (this.deferredTransfer) { - // this.deferredTransfer.transfer.recipient contains the publicKey of the recipient return this.deferredTransfer.transfer.recipient } return undefined diff --git a/dlt-connector/src/data/proto/TransactionBody.builder.ts b/dlt-connector/src/data/proto/TransactionBody.builder.ts index cf9479015..22d943d48 100644 --- a/dlt-connector/src/data/proto/TransactionBody.builder.ts +++ b/dlt-connector/src/data/proto/TransactionBody.builder.ts @@ -7,6 +7,7 @@ import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { LogError } from '@/server/LogError' import { CommunityRoot } from './3_3/CommunityRoot' +import { CrossGroupType } from './3_3/enum/CrossGroupType' import { GradidoCreation } from './3_3/GradidoCreation' import { GradidoTransfer } from './3_3/GradidoTransfer' import { TransactionBody } from './3_3/TransactionBody' @@ -78,6 +79,26 @@ export class TransactionBodyBuilder { return this } + public setCrossGroupType(type: CrossGroupType): this { + if (!this.body) { + throw new LogError( + 'body is undefined, please call fromTransactionDraft or fromCommunityDraft before', + ) + } + this.body.type = type + return this + } + + public setOtherGroup(otherGroup: string): this { + if (!this.body) { + throw new LogError( + 'body is undefined, please call fromTransactionDraft or fromCommunityDraft before', + ) + } + this.body.otherGroup = otherGroup + return this + } + public fromTransactionDraft(transactionDraft: TransactionDraft): TransactionBodyBuilder { this.body = new TransactionBody(transactionDraft) // TODO: load pubkeys for sender and recipient user from db diff --git a/dlt-connector/src/data/proto/transactionBody.logic.ts b/dlt-connector/src/data/proto/transactionBody.logic.ts deleted file mode 100644 index 99dbc2ed6..000000000 --- a/dlt-connector/src/data/proto/transactionBody.logic.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { InputTransactionType } from '@/graphql/enum/InputTransactionType' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' -import { TransactionError } from '@/graphql/model/TransactionError' - -import { CrossGroupType } from './3_3/enum/CrossGroupType' - -export const determineCrossGroupType = ({ - senderUser, - recipientUser, - type, -}: TransactionDraft): CrossGroupType => { - if ( - !recipientUser.communityUuid || - recipientUser.communityUuid === '' || - senderUser.communityUuid === recipientUser.communityUuid || - type === InputTransactionType.CREATION - ) { - return CrossGroupType.LOCAL - } else if (type === InputTransactionType.SEND) { - return CrossGroupType.INBOUND - } else if (type === InputTransactionType.RECEIVE) { - return CrossGroupType.OUTBOUND - } - throw new TransactionError( - TransactionErrorType.NOT_IMPLEMENTED_YET, - 'cannot determine CrossGroupType', - ) -} - -export const determineOtherGroup = ( - type: CrossGroupType, - { senderUser, recipientUser }: TransactionDraft, -): string => { - switch (type) { - case CrossGroupType.LOCAL: - return '' - case CrossGroupType.INBOUND: - if (!recipientUser.communityUuid) { - throw new TransactionError( - TransactionErrorType.MISSING_PARAMETER, - 'missing recipient user community id for cross group transaction', - ) - } - return recipientUser.communityUuid - case CrossGroupType.OUTBOUND: - if (!senderUser.communityUuid) { - throw new TransactionError( - TransactionErrorType.MISSING_PARAMETER, - 'missing sender user community id for cross group transaction', - ) - } - return senderUser.communityUuid - case CrossGroupType.CROSS: - throw new TransactionError(TransactionErrorType.NOT_IMPLEMENTED_YET, 'not implemented yet') - } -} diff --git a/dlt-connector/src/graphql/input/TransactionDraft.ts b/dlt-connector/src/graphql/input/TransactionDraft.ts index 7c43e9554..541797565 100755 --- a/dlt-connector/src/graphql/input/TransactionDraft.ts +++ b/dlt-connector/src/graphql/input/TransactionDraft.ts @@ -14,12 +14,12 @@ export class TransactionDraft { @Field(() => UserIdentifier) @IsObject() @ValidateNested() - senderUser: UserIdentifier + user: UserIdentifier @Field(() => UserIdentifier) @IsObject() @ValidateNested() - recipientUser: UserIdentifier + linkedUser: UserIdentifier @Field(() => Int) @IsPositive() diff --git a/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts b/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts index b5d5ee126..6eb443e21 100644 --- a/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts +++ b/dlt-connector/src/graphql/resolver/TransactionsResolver.test.ts @@ -62,8 +62,8 @@ const createUserStoreAccount = async (uuid: string): Promise => } describe('Transaction Resolver Test', () => { - let senderUser: UserIdentifier - let recipientUser: UserIdentifier + let user: UserIdentifier + let linkedUser: UserIdentifier beforeAll(async () => { await TestDB.instance.setupTestDB() apolloTestServer = await createApolloTestServer() @@ -74,8 +74,8 @@ describe('Transaction Resolver Test', () => { communityDraft.createdAt = new Date().toString() const addCommunityContext = new AddCommunityContext(communityDraft) await addCommunityContext.run() - senderUser = await createUserStoreAccount('0ec72b74-48c2-446f-91ce-31ad7d9f4d65') - recipientUser = await createUserStoreAccount('ddc8258e-fcb5-4e48-8d1d-3a07ec371dbe') + user = await createUserStoreAccount('0ec72b74-48c2-446f-91ce-31ad7d9f4d65') + linkedUser = await createUserStoreAccount('ddc8258e-fcb5-4e48-8d1d-3a07ec371dbe') }) afterAll(async () => { @@ -88,8 +88,8 @@ describe('Transaction Resolver Test', () => { 'mutation ($input: TransactionDraft!) { sendTransaction(data: $input) {succeed, recipe { id, topic }} }', variables: { input: { - senderUser, - recipientUser, + user, + linkedUser, type: getEnumValue(InputTransactionType, InputTransactionType.SEND), amount: '10', createdAt: '2012-04-17T17:12:00Z', @@ -110,8 +110,8 @@ describe('Transaction Resolver Test', () => { 'mutation ($input: TransactionDraft!) { sendTransaction(data: $input) {error {type, message}, succeed} }', variables: { input: { - senderUser, - recipientUser, + user, + linkedUser, type: 'INVALID', amount: '10', createdAt: '2012-04-17T17:12:00Z', @@ -136,8 +136,8 @@ describe('Transaction Resolver Test', () => { 'mutation ($input: TransactionDraft!) { sendTransaction(data: $input) {error {type, message}, succeed} }', variables: { input: { - senderUser, - recipientUser, + user, + linkedUser, type: getEnumValue(InputTransactionType, InputTransactionType.SEND), amount: 'no number', createdAt: '2012-04-17T17:12:00Z', @@ -162,8 +162,8 @@ describe('Transaction Resolver Test', () => { 'mutation ($input: TransactionDraft!) { sendTransaction(data: $input) {error {type, message}, succeed} }', variables: { input: { - senderUser, - recipientUser, + user, + linkedUser, type: getEnumValue(InputTransactionType, InputTransactionType.SEND), amount: '10', createdAt: 'not valid', @@ -198,8 +198,8 @@ describe('Transaction Resolver Test', () => { 'mutation ($input: TransactionDraft!) { sendTransaction(data: $input) {error {type, message}, succeed} }', variables: { input: { - senderUser, - recipientUser, + user, + linkedUser, type: getEnumValue(InputTransactionType, InputTransactionType.CREATION), amount: '10', createdAt: '2012-04-17T17:12:00Z', diff --git a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts index 0b134b97c..256cfe1a5 100644 --- a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts @@ -28,8 +28,7 @@ export class HomeCommunityRole extends CommunityRole { this.self.aufAccount = AccountFactory.createAufAccount(keyPair, this.self.createdAt) this.self.gmwAccount = AccountFactory.createGmwAccount(keyPair, this.self.createdAt) - const transactionRecipeContext = new CreateTransactionRecipeContext(communityDraft) - transactionRecipeContext.setCommunity(this.self) + const transactionRecipeContext = new CreateTransactionRecipeContext(communityDraft, this.self) await transactionRecipeContext.run() this.transactionRecipe = transactionRecipeContext.getTransactionRecipe() } diff --git a/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransaction.role.ts new file mode 100644 index 000000000..62fcf90de --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/transaction/AbstractTransaction.role.ts @@ -0,0 +1,62 @@ +import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType' +import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { UserIdentifier } from '@/graphql/input/UserIdentifier' +import { TransactionError } from '@/graphql/model/TransactionError' + +export abstract class AbstractTransactionRole { + // eslint-disable-next-line no-useless-constructor + public constructor(protected self: TransactionDraft) {} + + abstract getSigningUser(): UserIdentifier + abstract getRecipientUser(): UserIdentifier + abstract getCrossGroupType(): CrossGroupType + + public isCrossGroupTransaction(): boolean { + return ( + this.self.user.communityUuid !== this.self.linkedUser.communityUuid && + this.self.linkedUser.communityUuid !== '' + ) + } + + /** + * otherGroup is the group/community on which this part of the transaction isn't stored + * Alice from 'gdd1' Send 10 GDD to Bob in 'gdd2' + * OUTBOUND came from sender, stored on sender community blockchain + * OUTBOUND: stored on 'gdd1', otherGroup: 'gdd2' + * INBOUND: goes to receiver, stored on receiver community blockchain + * INBOUND: stored on 'gdd2', otherGroup: 'gdd1' + * @returns + */ + public getOtherGroup(): string { + let user: UserIdentifier + const type = this.getCrossGroupType() + switch (type) { + case CrossGroupType.LOCAL: + return '' + case CrossGroupType.INBOUND: + user = this.getSigningUser() + if (!user.communityUuid) { + throw new TransactionError( + TransactionErrorType.MISSING_PARAMETER, + 'missing sender/signing user community id for cross group transaction', + ) + } + return user.communityUuid + case CrossGroupType.OUTBOUND: + user = this.getRecipientUser() + if (!user.communityUuid) { + throw new TransactionError( + TransactionErrorType.MISSING_PARAMETER, + 'missing recipient user community id for cross group transaction', + ) + } + return user.communityUuid + default: + throw new TransactionError( + TransactionErrorType.NOT_IMPLEMENTED_YET, + `type not implemented yet ${type}`, + ) + } + } +} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts index 6c65e9036..8fa3dc443 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts @@ -1,12 +1,17 @@ import { Community } from '@entity/Community' import { Transaction } from '@entity/Transaction' +import { InputTransactionType } from '@/graphql/enum/InputTransactionType' import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { TransactionError } from '@/graphql/model/TransactionError' +import { AbstractTransactionRole } from './AbstractTransaction.role' import { CommunityRootTransactionRole } from './CommunityRootTransaction.role' +import { CreationTransactionRole } from './CreationTransaction.role' +import { ReceiveTransactionRole } from './ReceiveTransaction.role' +import { SendTransactionRole } from './SendTransaction.role' import { TransactionRecipeRole } from './TransactionRecipe.role' /** @@ -16,22 +21,11 @@ import { TransactionRecipeRole } from './TransactionRecipe.role' export class CreateTransactionRecipeContext { private transactionRecipeRole: TransactionRecipeRole - private community?: Community - public constructor(private draft: CommunityDraft | TransactionDraft) { - if (draft instanceof CommunityDraft) { - this.transactionRecipeRole = new CommunityRootTransactionRole() - } else if (draft instanceof TransactionDraft) { - this.transactionRecipeRole = new TransactionRecipeRole() - } - } - - public setCommunity(community: Community) { - this.community = community - } - - public getCommunity(): Community | undefined { - return this.community - } + // eslint-disable-next-line no-useless-constructor + public constructor( + private draft: CommunityDraft | TransactionDraft, + private community?: Community, + ) {} public getTransactionRecipe(): Transaction { return this.transactionRecipeRole.getTransaction() @@ -43,7 +37,20 @@ export class CreateTransactionRecipeContext { public async run(): Promise { if (this.draft instanceof TransactionDraft) { this.transactionRecipeRole = new TransactionRecipeRole() - await this.transactionRecipeRole.create(this.draft) + // contain logic for translation from backend to dlt-connector format + let transactionTypeRole: AbstractTransactionRole + switch (this.draft.type) { + case InputTransactionType.CREATION: + transactionTypeRole = new CreationTransactionRole(this.draft) + break + case InputTransactionType.SEND: + transactionTypeRole = new SendTransactionRole(this.draft) + break + case InputTransactionType.RECEIVE: + transactionTypeRole = new ReceiveTransactionRole(this.draft) + break + } + await this.transactionRecipeRole.create(this.draft, transactionTypeRole) return true } else if (this.draft instanceof CommunityDraft) { if (!this.community) { diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreationTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreationTransaction.role.ts new file mode 100644 index 000000000..7b82f8805 --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/transaction/CreationTransaction.role.ts @@ -0,0 +1,18 @@ +import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType' +import { UserIdentifier } from '@/graphql/input/UserIdentifier' + +import { AbstractTransactionRole } from './AbstractTransaction.role' + +export class CreationTransactionRole extends AbstractTransactionRole { + public getSigningUser(): UserIdentifier { + return this.self.linkedUser + } + + public getRecipientUser(): UserIdentifier { + return this.self.user + } + + public getCrossGroupType(): CrossGroupType { + return CrossGroupType.LOCAL + } +} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/ReceiveTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/ReceiveTransaction.role.ts new file mode 100644 index 000000000..bf7c69f0e --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/transaction/ReceiveTransaction.role.ts @@ -0,0 +1,21 @@ +import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType' +import { UserIdentifier } from '@/graphql/input/UserIdentifier' + +import { AbstractTransactionRole } from './AbstractTransaction.role' + +export class ReceiveTransactionRole extends AbstractTransactionRole { + public getSigningUser(): UserIdentifier { + return this.self.linkedUser + } + + public getRecipientUser(): UserIdentifier { + return this.self.user + } + + public getCrossGroupType(): CrossGroupType { + if (this.isCrossGroupTransaction()) { + return CrossGroupType.INBOUND + } + return CrossGroupType.LOCAL + } +} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/SendTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/SendTransaction.role.ts new file mode 100644 index 000000000..927efdc24 --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/transaction/SendTransaction.role.ts @@ -0,0 +1,21 @@ +import { CrossGroupType } from '@/data/proto/3_3/enum/CrossGroupType' +import { UserIdentifier } from '@/graphql/input/UserIdentifier' + +import { AbstractTransactionRole } from './AbstractTransaction.role' + +export class SendTransactionRole extends AbstractTransactionRole { + public getSigningUser(): UserIdentifier { + return this.self.user + } + + public getRecipientUser(): UserIdentifier { + return this.self.linkedUser + } + + public getCrossGroupType(): CrossGroupType { + if (this.isCrossGroupTransaction()) { + return CrossGroupType.OUTBOUND + } + return CrossGroupType.LOCAL + } +} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts index 7f77a5e82..d36aa98cc 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts @@ -8,6 +8,8 @@ import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' import { TransactionDraft } from '@/graphql/input/TransactionDraft' import { TransactionError } from '@/graphql/model/TransactionError' +import { AbstractTransactionRole } from './AbstractTransaction.role' + export class TransactionRecipeRole { protected transactionBuilder: TransactionBuilder @@ -15,13 +17,16 @@ export class TransactionRecipeRole { this.transactionBuilder = new TransactionBuilder() } - public async create(transactionDraft: TransactionDraft): Promise { - const senderUser = transactionDraft.senderUser - const recipientUser = transactionDraft.recipientUser + public async create( + transactionDraft: TransactionDraft, + transactionTypeRole: AbstractTransactionRole, + ): Promise { + const signingUser = transactionTypeRole.getSigningUser() + const recipientUser = transactionTypeRole.getRecipientUser() // loading signing and recipient account // TODO: look for ways to use only one db call for both - const signingAccount = await UserRepository.findAccountByUserIdentifier(senderUser) + const signingAccount = await UserRepository.findAccountByUserIdentifier(signingUser) if (!signingAccount) { throw new TransactionError( TransactionErrorType.NOT_FOUND, @@ -40,13 +45,15 @@ export class TransactionRecipeRole { .setSigningAccount(signingAccount) .setRecipientAccount(recipientAccount) .fromTransactionDraft(transactionDraft) + .setCrossGroupType(transactionTypeRole.getCrossGroupType()) + .setOtherGroup(transactionTypeRole.getOtherGroup()) // build transaction entity this.transactionBuilder .fromTransactionBodyBuilder(transactionBodyBuilder) .addBackendTransaction(transactionDraft) - await this.transactionBuilder.setSenderCommunityFromSenderUser(senderUser) - if (recipientUser.communityUuid !== senderUser.communityUuid) { + await this.transactionBuilder.setSenderCommunityFromSenderUser(signingUser) + if (recipientUser.communityUuid !== signingUser.communityUuid) { await this.transactionBuilder.setOtherCommunityFromRecipientUser(recipientUser) } const transaction = this.transactionBuilder.getTransaction()