refactored process

This commit is contained in:
einhornimmond 2024-11-14 08:50:39 +01:00
parent de162647e5
commit 7ddd1cf922
17 changed files with 161 additions and 80 deletions

View File

@ -9,6 +9,7 @@ import { backendLogger as logger } from '@/server/logger'
// eslint-disable-next-line import/named, n/no-extraneous-import
import { TransactionDraft } from './model/TransactionDraft'
import { TransactionLinkDraft } from './model/TransactionLinkDraft'
import { TransactionResult } from './model/TransactionResult'
import { UserAccountDraft } from './model/UserAccountDraft'
@ -29,6 +30,23 @@ const sendTransaction = gql`
}
`
const deferredTransfer = gql`
mutation ($input: TransactionLinkDraft!) {
deferredTransfer(data: $input) {
error {
message
name
}
succeed
recipe {
createdAt
type
messageIdHex
}
}
}
`
const registerAddress = gql`
mutation ($input: UserAccountDraft!) {
registerAddress(data: $input) {
@ -114,6 +132,15 @@ export class DltConnectorClient {
return this.handleTransactionResult(result)
}
private async deferredTransfer(input: TransactionLinkDraft) {
const {
data: { deferredTransfer: result },
} = await this.client.rawRequest<{ deferredTransfer: TransactionResult }>(deferredTransfer, {
input,
})
return this.handleTransactionResult(result)
}
private async registerAddress(input: UserAccountDraft) {
const {
data: { registerAddress: result },
@ -128,7 +155,7 @@ export class DltConnectorClient {
* and update dltTransactionId of transaction in db with iota message id
*/
public async transmitTransaction(
input: TransactionDraft | UserAccountDraft,
input: TransactionDraft | UserAccountDraft | TransactionLinkDraft,
): Promise<TransactionResult | undefined> {
// we don't need the receive transactions, there contain basically the same data as the send transactions
if (
@ -144,6 +171,8 @@ export class DltConnectorClient {
return await this.sendTransaction(input)
} else if (input instanceof UserAccountDraft) {
return await this.registerAddress(input)
} else if (input instanceof TransactionLinkDraft) {
return await this.deferredTransfer(input)
} else {
throw new LogError('unhandled branch reached')
}

View File

@ -3,6 +3,7 @@ import { ObjectLiteral, OrderByCondition, SelectQueryBuilder } from '@dbTools/ty
import { DltTransaction } from '@entity/DltTransaction'
import { TransactionDraft } from '@dltConnector/model/TransactionDraft'
import { TransactionLinkDraft } from '@dltConnector/model/TransactionLinkDraft'
import { UserAccountDraft } from '@dltConnector/model/UserAccountDraft'
import { backendLogger as logger } from '@/server/logger'
@ -13,7 +14,11 @@ export abstract class AbstractTransactionToDltRole<T extends ObjectLiteral> {
// public interface
public abstract initWithLast(): Promise<this>
public abstract getTimestamp(): number
public abstract convertToGraphqlInput(): TransactionDraft | UserAccountDraft
public abstract convertToGraphqlInput():
| TransactionDraft
| UserAccountDraft
| TransactionLinkDraft
public getEntity(): T | null {
return this.self
}

View File

@ -2,6 +2,7 @@ import { DltTransaction } from '@entity/DltTransaction'
import { TransactionLink } from '@entity/TransactionLink'
import { TransactionDraft } from '@dltConnector/model/TransactionDraft'
import { TransactionLinkDraft } from '@dltConnector/model/TransactionLinkDraft'
import { UserAccountDraft } from '@dltConnector/model/UserAccountDraft'
import { LogError } from '@/server/LogError'
@ -29,11 +30,11 @@ export class TransactionLinkToDltRole extends AbstractTransactionToDltRole<Trans
return this.self.createdAt.getTime()
}
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft {
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft | TransactionLinkDraft {
if (!this.self) {
throw new LogError('try to create dlt entry for empty transaction link')
}
return new TransactionDraft(this.self)
return new TransactionLinkDraft(this.self)
}
protected setJoinId(dltTransaction: DltTransaction): void {

View File

@ -2,6 +2,7 @@ import { DltTransaction } from '@entity/DltTransaction'
import { Transaction } from '@entity/Transaction'
import { TransactionDraft } from '@dltConnector/model/TransactionDraft'
import { TransactionLinkDraft } from '@dltConnector/model/TransactionLinkDraft'
import { UserAccountDraft } from '@dltConnector/model/UserAccountDraft'
import { LogError } from '@/server/LogError'
@ -29,7 +30,7 @@ export class TransactionToDltRole extends AbstractTransactionToDltRole<Transacti
return this.self.balanceDate.getTime()
}
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft {
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft | TransactionLinkDraft {
if (!this.self) {
throw new LogError('try to create dlt entry for empty transaction')
}

View File

@ -2,6 +2,7 @@ import { DltTransaction } from '@entity/DltTransaction'
import { User } from '@entity/User'
import { TransactionDraft } from '@dltConnector/model/TransactionDraft'
import { TransactionLinkDraft } from '@dltConnector/model/TransactionLinkDraft'
import { UserAccountDraft } from '@dltConnector/model/UserAccountDraft'
import { LogError } from '@/server/LogError'
@ -29,7 +30,7 @@ export class UserToDltRole extends AbstractTransactionToDltRole<User> {
return this.self.createdAt.getTime()
}
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft {
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft | TransactionLinkDraft {
if (!this.self) {
throw new LogError('try to create dlt entry for empty transaction')
}

View File

@ -1,7 +0,0 @@
export class IdentifierSeed {
seed: string
constructor(seed: string) {
this.seed = seed
}
}

View File

@ -1,52 +1,39 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { Transaction } from '@entity/Transaction'
import { TransactionLink } from '@entity/TransactionLink'
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
import { LogError } from '@/server/LogError'
import { IdentifierSeed } from './IdentifierSeed'
import { UserIdentifier } from './UserIdentifier'
export class TransactionDraft {
user: UserIdentifier
linkedUser: UserIdentifier | IdentifierSeed
linkedUser: UserIdentifier
amount: string
type: string
createdAt: string
// only for creation transactions
targetDate?: string
// only for transaction links
timeoutDate?: string
constructor(transaction: Transaction | TransactionLink) {
constructor(transaction: Transaction) {
this.amount = transaction.amount.abs().toString()
if (transaction instanceof Transaction) {
if (
!transaction.linkedUserGradidoID ||
!transaction.linkedUserCommunityUuid ||
!transaction.userCommunityUuid
) {
throw new LogError(
`missing necessary field in transaction: ${transaction.id}, need linkedUserGradidoID, linkedUserCommunityUuid and userCommunityUuid`,
)
}
this.user = new UserIdentifier(transaction.userGradidoID, transaction.userCommunityUuid)
this.linkedUser = new UserIdentifier(
transaction.linkedUserGradidoID,
transaction.linkedUserCommunityUuid,
if (
!transaction.linkedUserGradidoID ||
!transaction.linkedUserCommunityUuid ||
!transaction.userCommunityUuid
) {
throw new LogError(
`missing necessary field in transaction: ${transaction.id}, need linkedUserGradidoID, linkedUserCommunityUuid and userCommunityUuid`,
)
this.createdAt = transaction.balanceDate.toISOString()
this.targetDate = transaction.creationDate?.toISOString()
this.type = TransactionTypeId[transaction.typeId]
} else if (transaction instanceof TransactionLink) {
const user = transaction.user
this.user = new UserIdentifier(user.gradidoID, user.communityUuid)
this.linkedUser = new IdentifierSeed(transaction.code)
this.createdAt = transaction.createdAt.toISOString()
this.type = TransactionTypeId[TransactionTypeId.LINK_SUMMARY]
this.timeoutDate = transaction.validUntil.toISOString()
}
this.user = new UserIdentifier(transaction.userGradidoID, transaction.userCommunityUuid)
this.linkedUser = new UserIdentifier(
transaction.linkedUserGradidoID,
transaction.linkedUserCommunityUuid,
)
this.createdAt = transaction.balanceDate.toISOString()
this.targetDate = transaction.creationDate?.toISOString()
this.type = TransactionTypeId[transaction.typeId]
}
}

View File

@ -0,0 +1,21 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { TransactionLink } from '@entity/TransactionLink'
import { UserIdentifier } from './UserIdentifier'
export class TransactionLinkDraft {
user: UserIdentifier
seed: string
amount: string
createdAt: string
timeoutDate: string
constructor(transaction: TransactionLink) {
this.amount = transaction.amount.abs().toString()
const user = transaction.user
this.user = new UserIdentifier(user.gradidoID, user.communityUuid)
this.seed = transaction.code
this.createdAt = transaction.createdAt.toISOString()
this.timeoutDate = transaction.validUntil.toISOString()
}
}

View File

@ -6,6 +6,9 @@ export enum InputTransactionType {
CREATION = 1,
SEND = 2,
RECEIVE = 3,
// This is a virtual property, never occurring on the database
DECAY = 4,
LINK_SUMMARY = 5,
}
registerEnumType(InputTransactionType, {

View File

@ -1,9 +1,15 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsString } from 'class-validator'
import { InputType, Field } from 'type-graphql'
import { Field, InputType } from 'type-graphql'
@InputType()
export class IdentifierSeed {
@Field(() => String)
@IsString()
seed: string
constructor(seed: string) {
this.seed = seed
}
}

View File

@ -1,11 +1,9 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { InputTransactionType } from '@enum/InputTransactionType'
import { isValidDateString, isValidNumberString } from '@validator/DateString'
import { IsEnum, IsObject, ValidateNested } from 'class-validator'
import { InputType, Field } from 'type-graphql'
import { InputTransactionType } from '@enum/InputTransactionType'
import { isValidDateString, isValidNumberString } from '@validator/DateString'
import { IdentifierSeed } from './IdentifierSeed'
import { UserIdentifier } from './UserIdentifier'
@InputType()
@ -18,7 +16,7 @@ export class TransactionDraft {
@Field(() => UserIdentifier)
@IsObject()
@ValidateNested()
linkedUser: UserIdentifier | IdentifierSeed
linkedUser: UserIdentifier
@Field(() => String)
@isValidNumberString()
@ -36,9 +34,4 @@ export class TransactionDraft {
@Field(() => String, { nullable: true })
@isValidDateString()
targetDate?: string
// only for transaction links
@Field(() => String, { nullable: true })
@isValidDateString()
timeoutDate?: string
}

View File

@ -0,0 +1,30 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { isValidDateString, isValidNumberString } from '@validator/DateString'
import { IsObject, IsString, ValidateNested } from 'class-validator'
import { InputType, Field } from 'type-graphql'
import { UserIdentifier } from './UserIdentifier'
@InputType()
export class TransactionLinkDraft {
@Field(() => UserIdentifier)
@IsObject()
@ValidateNested()
user: UserIdentifier
@Field(() => String)
@IsString()
seed: string
@Field(() => String)
@isValidNumberString()
amount: string
@Field(() => String)
@isValidDateString()
createdAt: string
@Field(() => String)
@isValidDateString()
timeoutDate: string
}

View File

@ -1,9 +1,9 @@
import { TransactionLinkDraft } from '@input/TransactionLinkDraft'
import { Resolver, Arg, Mutation } from 'type-graphql'
import { TransactionDraft } from '@input/TransactionDraft'
import { SendToIotaContext } from '@/interactions/sendToIota/SendToIota.context'
import { TransactionDraft } from '../input/TransactionDraft'
import { TransactionError } from '../model/TransactionError'
import { TransactionResult } from '../model/TransactionResult'
@ -25,4 +25,21 @@ export class TransactionResolver {
}
}
}
@Mutation(() => TransactionResult)
async deferredTransfer(
@Arg('data')
transactionLinkDraft: TransactionLinkDraft,
): Promise<TransactionResult> {
try {
return await SendToIotaContext(transactionLinkDraft)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error instanceof TransactionError) {
return new TransactionResult(error)
} else {
throw error
}
}
}
}

View File

@ -1,6 +1,5 @@
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
import { IdentifierSeed } from '@/graphql/input/IdentifierSeed'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { LogError } from '@/server/LogError'
@ -22,9 +21,6 @@ export class CreationTransactionRole extends AbstractTransactionRole {
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (this.self.linkedUser instanceof IdentifierSeed) {
throw new LogError('invalid recipient, it is a IdentifierSeed instead of a UserIdentifier')
}
if (!this.self.targetDate) {
throw new LogError('target date missing for creation transaction')
}

View File

@ -1,27 +1,30 @@
import { GradidoTransactionBuilder, GradidoTransfer, TransferAmount } from 'gradido-blockchain-js'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { IdentifierSeed } from '@/graphql/input/IdentifierSeed'
import { TransactionLinkDraft } from '@/graphql/input/TransactionLinkDraft'
import { LogError } from '@/server/LogError'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
import { TransferTransactionRole } from './TransferTransaction.role'
import { AbstractTransactionRole } from './AbstractTransaction.role'
export class DeferredTransferTransactionRole extends AbstractTransactionRole {
constructor(protected self: TransactionLinkDraft) {
super()
}
getSenderCommunityUuid(): string {
return this.self.user.communityUuid
}
export class DeferredTransferTransactionRole extends TransferTransactionRole {
getRecipientCommunityUuid(): string {
throw new LogError('cannot be used as cross group transaction')
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (this.self.linkedUser instanceof UserIdentifier) {
throw new LogError('invalid recipient, it is a UserIdentifier instead of a IdentifierSeed')
}
if (!this.self.timeoutDate) {
throw new LogError('timeoutDate date missing for deferred transfer transaction')
}
const builder = new GradidoTransactionBuilder()
const senderKeyPair = await KeyPairCalculation(this.self.user)
const recipientKeyPair = await KeyPairCalculation(this.self.linkedUser)
const recipientKeyPair = await KeyPairCalculation(new IdentifierSeed(this.self.seed))
builder
.setCreatedAt(new Date(this.self.createdAt))

View File

@ -12,6 +12,7 @@ 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 { TransactionLinkDraft } from '@/graphql/input/TransactionLinkDraft'
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
import { TransactionError } from '@/graphql/model/TransactionError'
import { TransactionRecipe } from '@/graphql/model/TransactionRecipe'
@ -23,6 +24,7 @@ import { uuid4ToHash } from '@/utils/typeConverter'
import { AbstractTransactionRole } from './AbstractTransaction.role'
import { CommunityRootTransactionRole } from './CommunityRootTransaction.role'
import { CreationTransactionRole } from './CreationTransaction.role'
import { DeferredTransferTransactionRole } from './DeferredTransferTransaction.role'
import { RegisterAddressTransactionRole } from './RegisterAddressTransaction.role'
import { TransferTransactionRole } from './TransferTransaction.role'
@ -32,7 +34,7 @@ import { TransferTransactionRole } from './TransferTransaction.role'
* send every transaction only once to iota!
*/
export async function SendToIotaContext(
input: TransactionDraft | UserAccountDraft | CommunityDraft,
input: TransactionDraft | UserAccountDraft | CommunityDraft | TransactionLinkDraft,
): Promise<TransactionResult> {
const validate = (transaction: GradidoTransaction): void => {
try {
@ -81,6 +83,8 @@ export async function SendToIotaContext(
} else {
throw new LogError('not supported transaction type')
}
} else if (input instanceof TransactionLinkDraft) {
role = new DeferredTransferTransactionRole(input)
} else if (input instanceof UserAccountDraft) {
role = new RegisterAddressTransactionRole(input)
} else if (input instanceof CommunityDraft) {

View File

@ -1,8 +1,6 @@
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
import { IdentifierSeed } from '@/graphql/input/IdentifierSeed'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { LogError } from '@/server/LogError'
import { uuid4ToHash } from '@/utils/typeConverter'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
@ -19,17 +17,10 @@ export class TransferTransactionRole extends AbstractTransactionRole {
}
getRecipientCommunityUuid(): string {
if (this.self.linkedUser instanceof IdentifierSeed) {
throw new LogError('invalid recipient, it is a IdentifierSeed instead of a UserIdentifier')
}
return this.self.linkedUser.communityUuid
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (this.self.linkedUser instanceof IdentifierSeed) {
throw new LogError('invalid recipient, it is a IdentifierSeed instead of a UserIdentifier')
}
const builder = new GradidoTransactionBuilder()
const senderKeyPair = await KeyPairCalculation(this.self.user)
const recipientKeyPair = await KeyPairCalculation(this.self.linkedUser)