mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 09:56:05 +00:00
move translation logic from backend into dlt-connector, refactor
This commit is contained in:
parent
75a7481a1b
commit
ed4e46a14a
@ -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<TransactionBody> {
|
||||
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<TransactionBody> {
|
||||
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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')
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
|
||||
@ -62,8 +62,8 @@ const createUserStoreAccount = async (uuid: string): Promise<UserIdentifier> =>
|
||||
}
|
||||
|
||||
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',
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<boolean> {
|
||||
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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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<TransactionRecipeRole> {
|
||||
const senderUser = transactionDraft.senderUser
|
||||
const recipientUser = transactionDraft.recipientUser
|
||||
public async create(
|
||||
transactionDraft: TransactionDraft,
|
||||
transactionTypeRole: AbstractTransactionRole,
|
||||
): Promise<TransactionRecipeRole> {
|
||||
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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user