refactor, pack in one graphql in put model, add redeeming transaction link in dlt

This commit is contained in:
einhornimmond 2024-11-14 11:20:43 +01:00
parent 7ddd1cf922
commit a24ed8ef98
40 changed files with 606 additions and 409 deletions

View File

@ -109,21 +109,6 @@ describe('transmitTransaction', () => {
expect(result).toBe(false)
})
*/
it('invalid transaction type', async () => {
const localTransaction = new DbTransaction()
localTransaction.typeId = 12
try {
await DltConnectorClient.getInstance()?.transmitTransaction(
new TransactionDraft(localTransaction),
)
} catch (e) {
expect(e).toMatchObject(
new LogError('invalid transaction type id: ' + localTransaction.typeId.toString()),
)
}
})
/*
it.skip('should transmit the transaction and update the dltTransactionId in the database', async () => {
await transaction.save()

View File

@ -1,17 +1,11 @@
import { gql, GraphQLClient } from 'graphql-request'
// eslint-disable-next-line import/named, n/no-extraneous-import
import { FetchError } from 'node-fetch'
import { CONFIG } from '@/config'
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
import { LogError } from '@/server/LogError'
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'
const sendTransaction = gql`
mutation ($input: TransactionDraft!) {
@ -30,40 +24,6 @@ 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) {
error {
message
name
}
succeed
recipe {
createdAt
type
messageIdHex
}
}
}
`
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
// and ../federation/client/FederationClientFactory.ts
/**
@ -116,75 +76,17 @@ export class DltConnectorClient {
return DltConnectorClient.instance
}
private handleTransactionResult(result: TransactionResult) {
if (result.error) {
throw new Error(result.error.message)
}
return result
}
private async sendTransaction(input: TransactionDraft) {
/**
* transmit transaction via dlt-connector to iota
* and update dltTransactionId of transaction in db with iota message id
*/
public async sendTransaction(input: TransactionDraft): Promise<TransactionResult | undefined> {
logger.debug('transmit transaction or user to dlt connector', input)
const {
data: { sendTransaction: result },
} = await this.client.rawRequest<{ sendTransaction: TransactionResult }>(sendTransaction, {
input,
})
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 },
} = await this.client.rawRequest<{ registerAddress: TransactionResult }>(registerAddress, {
input,
})
return this.handleTransactionResult(result)
}
/**
* transmit transaction via dlt-connector to iota
* and update dltTransactionId of transaction in db with iota message id
*/
public async transmitTransaction(
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 (
input instanceof TransactionDraft &&
TransactionTypeId[input.type as keyof typeof TransactionTypeId] === TransactionTypeId.RECEIVE
) {
return
}
try {
logger.debug('transmit transaction or user to dlt connector', input)
if (input instanceof TransactionDraft) {
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')
}
} catch (e) {
logger.error(e)
if (e instanceof FetchError) {
throw e
} else if (e instanceof Error) {
throw new LogError(`from dlt-connector: ${e.message}`)
} else {
throw new LogError('Exception sending transfer transaction to dlt-connector', e)
}
}
return result
}
}

View File

@ -1,11 +1,18 @@
import { registerEnumType } from 'type-graphql'
/**
* Transaction Types on Blockchain
*/
export enum TransactionType {
GRADIDO_TRANSFER = 1,
GRADIDO_CREATION = 2,
GROUP_FRIENDS_UPDATE = 3,
REGISTER_ADDRESS = 4,
GRADIDO_DEFERRED_TRANSFER = 5,
COMMUNITY_ROOT = 6,
GRADIDO_TRANSFER = 'GRADIDO_TRANSFER',
GRADIDO_CREATION = 'GRADIDO_CREATION',
GROUP_FRIENDS_UPDATE = 'GROUP_FRIENDS_UPDATE',
REGISTER_ADDRESS = 'REGISTER_ADDRESS',
GRADIDO_DEFERRED_TRANSFER = 'GRADIDO_DEFERRED_TRANSFER',
COMMUNITY_ROOT = 'COMMUNITY_ROOT',
}
registerEnumType(TransactionType, {
name: 'TransactionType', // this one is mandatory
description: 'Type of the transaction', // this one is optional
})

View File

@ -3,8 +3,6 @@ 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'
@ -14,10 +12,7 @@ export abstract class AbstractTransactionToDltRole<T extends ObjectLiteral> {
// public interface
public abstract initWithLast(): Promise<this>
public abstract getTimestamp(): number
public abstract convertToGraphqlInput():
| TransactionDraft
| UserAccountDraft
| TransactionLinkDraft
public abstract convertToGraphqlInput(): TransactionDraft
public getEntity(): T | null {
return this.self
@ -48,7 +43,7 @@ export abstract class AbstractTransactionToDltRole<T extends ObjectLiteral> {
qb: SelectQueryBuilder<T>,
joinCondition: string,
orderBy: OrderByCondition,
): Promise<T | null> {
): SelectQueryBuilder<T> {
return qb
.leftJoin(DltTransaction, 'dltTransaction', joinCondition)
.where('dltTransaction.user_id IS NULL')
@ -56,7 +51,6 @@ export abstract class AbstractTransactionToDltRole<T extends ObjectLiteral> {
.andWhere('dltTransaction.transaction_link_Id IS NULL')
.orderBy(orderBy)
.limit(1)
.getOne()
}
protected createDltTransactionEntry(messageId: string, error: string | null): DltTransaction {

View File

@ -1,9 +1,11 @@
import { DltTransaction } from '@entity/DltTransaction'
import { TransactionLink } from '@entity/TransactionLink'
import { TransactionType } from '@dltConnector/enum/TransactionType'
import { CommunityUser } from '@dltConnector/model/CommunityUser'
import { IdentifierSeed } from '@dltConnector/model/IdentifierSeed'
import { TransactionDraft } from '@dltConnector/model/TransactionDraft'
import { TransactionLinkDraft } from '@dltConnector/model/TransactionLinkDraft'
import { UserAccountDraft } from '@dltConnector/model/UserAccountDraft'
import { UserIdentifier } from '@dltConnector/model/UserIdentifier'
import { LogError } from '@/server/LogError'
@ -19,7 +21,7 @@ export class TransactionLinkToDltRole extends AbstractTransactionToDltRole<Trans
'TransactionLink.id = dltTransaction.transactionLinkId',
// eslint-disable-next-line camelcase
{ TransactionLink_createdAt: 'ASC', User_id: 'ASC' },
)
).getOne()
return this
}
@ -30,11 +32,19 @@ export class TransactionLinkToDltRole extends AbstractTransactionToDltRole<Trans
return this.self.createdAt.getTime()
}
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft | TransactionLinkDraft {
public convertToGraphqlInput(): TransactionDraft {
if (!this.self) {
throw new LogError('try to create dlt entry for empty transaction link')
}
return new TransactionLinkDraft(this.self)
const draft = new TransactionDraft()
draft.amount = this.self.amount.abs().toString()
const user = this.self.user
draft.user = new UserIdentifier(user.communityUuid, new CommunityUser(user.gradidoID))
draft.linkedUser = new UserIdentifier(user.communityUuid, new IdentifierSeed(this.self.code))
draft.createdAt = this.self.createdAt.toISOString()
draft.timeoutDate = this.self.validUntil.toISOString()
draft.type = TransactionType.GRADIDO_DEFERRED_TRANSFER
return draft
}
protected setJoinId(dltTransaction: DltTransaction): void {

View File

@ -1,10 +1,13 @@
import { DltTransaction } from '@entity/DltTransaction'
import { Transaction } from '@entity/Transaction'
import { TransactionType } from '@dltConnector/enum/TransactionType'
import { CommunityUser } from '@dltConnector/model/CommunityUser'
import { IdentifierSeed } from '@dltConnector/model/IdentifierSeed'
import { TransactionDraft } from '@dltConnector/model/TransactionDraft'
import { TransactionLinkDraft } from '@dltConnector/model/TransactionLinkDraft'
import { UserAccountDraft } from '@dltConnector/model/UserAccountDraft'
import { UserIdentifier } from '@dltConnector/model/UserIdentifier'
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
import { LogError } from '@/server/LogError'
import { AbstractTransactionToDltRole } from './AbstractTransactionToDlt.role'
@ -15,11 +18,17 @@ import { AbstractTransactionToDltRole } from './AbstractTransactionToDlt.role'
export class TransactionToDltRole extends AbstractTransactionToDltRole<Transaction> {
async initWithLast(): Promise<this> {
this.self = await this.createQueryForPendingItems(
Transaction.createQueryBuilder(),
Transaction.createQueryBuilder().leftJoinAndSelect(
'Transaction.transactionLink',
'transactionLink',
),
'Transaction.id = dltTransaction.transactionId',
// eslint-disable-next-line camelcase
{ balance_date: 'ASC', Transaction_id: 'ASC' },
)
// we don't need the receive transactions, there contain basically the same data as the send transactions
.andWhere('transaction.typeId NOT :typeId', { typeId: TransactionTypeId.RECEIVE })
.getOne()
return this
}
@ -30,11 +39,53 @@ export class TransactionToDltRole extends AbstractTransactionToDltRole<Transacti
return this.self.balanceDate.getTime()
}
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft | TransactionLinkDraft {
public convertToGraphqlInput(): TransactionDraft {
if (!this.self) {
throw new LogError('try to create dlt entry for empty transaction')
}
return new TransactionDraft(this.self)
const draft = new TransactionDraft()
draft.amount = this.self.amount.abs().toString()
if (
!this.self.linkedUserGradidoID ||
!this.self.linkedUserCommunityUuid ||
!this.self.userCommunityUuid
) {
throw new LogError(
`missing necessary field in transaction: ${this.self.id}, need linkedUserGradidoID, linkedUserCommunityUuid and userCommunityUuid`,
)
}
// it is a redeem of a transaction link?
const transactionLink = this.self.transactionLink
if (transactionLink) {
draft.user = new UserIdentifier(
this.self.userCommunityUuid,
new IdentifierSeed(transactionLink.code),
)
} else {
draft.user = new UserIdentifier(
this.self.userCommunityUuid,
new CommunityUser(this.self.userGradidoID),
)
}
draft.linkedUser = new UserIdentifier(
this.self.linkedUserCommunityUuid,
new CommunityUser(this.self.linkedUserGradidoID),
)
draft.createdAt = this.self.balanceDate.toISOString()
draft.targetDate = this.self.creationDate?.toISOString()
switch (this.self.typeId as TransactionTypeId) {
case TransactionTypeId.CREATION:
draft.type = TransactionType.GRADIDO_CREATION
break
case TransactionTypeId.SEND:
case TransactionTypeId.RECEIVE:
draft.type = TransactionType.GRADIDO_TRANSFER
break
default:
throw new LogError('wrong role for type', this.self.typeId as TransactionTypeId)
}
return draft
}
protected setJoinId(dltTransaction: DltTransaction): void {

View File

@ -1,9 +1,11 @@
import { DltTransaction } from '@entity/DltTransaction'
import { User } from '@entity/User'
import { AccountType } from '@dltConnector/enum/AccountType'
import { TransactionType } from '@dltConnector/enum/TransactionType'
import { CommunityUser } from '@dltConnector/model/CommunityUser'
import { TransactionDraft } from '@dltConnector/model/TransactionDraft'
import { TransactionLinkDraft } from '@dltConnector/model/TransactionLinkDraft'
import { UserAccountDraft } from '@dltConnector/model/UserAccountDraft'
import { UserIdentifier } from '@dltConnector/model/UserIdentifier'
import { LogError } from '@/server/LogError'
@ -19,7 +21,7 @@ export class UserToDltRole extends AbstractTransactionToDltRole<User> {
'User.id = dltTransaction.userId',
// eslint-disable-next-line camelcase
{ User_created_at: 'ASC', User_id: 'ASC' },
)
).getOne()
return this
}
@ -30,11 +32,16 @@ export class UserToDltRole extends AbstractTransactionToDltRole<User> {
return this.self.createdAt.getTime()
}
public convertToGraphqlInput(): TransactionDraft | UserAccountDraft | TransactionLinkDraft {
public convertToGraphqlInput(): TransactionDraft {
if (!this.self) {
throw new LogError('try to create dlt entry for empty transaction')
}
return new UserAccountDraft(this.self)
const draft = new TransactionDraft()
draft.user = new UserIdentifier(this.self.communityUuid, new CommunityUser(this.self.gradidoID))
draft.createdAt = this.self.createdAt.toISOString()
draft.accountType = AccountType.COMMUNITY_HUMAN
draft.type = TransactionType.REGISTER_ADDRESS
return draft
}
protected setJoinId(dltTransaction: DltTransaction): void {

View File

@ -1,12 +1,9 @@
import { EntityPropertyNotFoundError, QueryFailedError, TypeORMError } from '@dbTools/typeorm'
import { Transaction } from '@entity/Transaction'
import { TransactionLink } from '@entity/TransactionLink'
import { User } from '@entity/User'
// eslint-disable-next-line import/named, n/no-extraneous-import
import { FetchError } from 'node-fetch'
import { DltConnectorClient } from '@/apis/dltConnector/DltConnectorClient'
import { TransactionResult } from '@/apis/dltConnector/model/TransactionResult'
import { backendLogger as logger } from '@/server/logger'
import { AbstractTransactionToDltRole } from './AbstractTransactionToDlt.role'
import { TransactionLinkToDltRole } from './TransactionLinkToDlt.role'
@ -40,21 +37,17 @@ export async function transactionToDlt(dltConnector: DltConnectorClient): Promis
if (!pendingTransaction) {
break
}
let result: TransactionResult | undefined
let messageId = ''
let error: string | null = null
try {
result = await dltConnector.transmitTransaction(
pendingTransactionRole.convertToGraphqlInput(),
)
if (result?.succeed && result.recipe) {
messageId = result.recipe.messageIdHex
} else {
error = 'skipped'
}
} catch (e) {
error = e instanceof Error ? e.message : String(e)
const result = await dltConnector.sendTransaction(
pendingTransactionRole.convertToGraphqlInput(),
)
if (result?.succeed && result.recipe) {
messageId = result.recipe.messageIdHex
} else if (result?.error) {
error = result.error.message
logger.error('error from dlt-connector', result.error)
}
await pendingTransactionRole.saveTransactionResult(messageId, error)

View File

@ -0,0 +1,10 @@
export class CommunityUser {
// for community user, uuid and communityUuid used
uuid: string
accountNr?: number
constructor(uuid: string, accountNr?: number) {
this.uuid = uuid
this.accountNr = accountNr
}
}

View File

@ -0,0 +1,9 @@
// https://www.npmjs.com/package/@apollo/protobufjs
export class IdentifierSeed {
seed: string
constructor(seed: string) {
this.seed = seed
}
}

View File

@ -1,39 +1,21 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { Transaction } from '@entity/Transaction'
import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId'
import { LogError } from '@/server/LogError'
import { AccountType } from '@dltConnector/enum/AccountType'
import { TransactionType } from '@dltConnector/enum/TransactionType'
import { UserIdentifier } from './UserIdentifier'
export class TransactionDraft {
user: UserIdentifier
linkedUser: UserIdentifier
amount: string
type: string
// not used for simply register address
linkedUser?: UserIdentifier
// not used for register address
amount?: string
type: TransactionType
createdAt: string
// only for creation transactions
// only for creation transaction
targetDate?: string
constructor(transaction: Transaction) {
this.amount = transaction.amount.abs().toString()
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,
)
this.createdAt = transaction.balanceDate.toISOString()
this.targetDate = transaction.creationDate?.toISOString()
this.type = TransactionTypeId[transaction.typeId]
}
// only for deferred transaction
timeoutDate?: string
// only for register address
accountType?: AccountType
}

View File

@ -1,21 +0,0 @@
// 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

@ -1,17 +0,0 @@
import { User } from '@entity/User'
import { AccountType } from '@/apis/dltConnector/enum/AccountType'
import { UserIdentifier } from './UserIdentifier'
export class UserAccountDraft {
user: UserIdentifier
createdAt: string
accountType: AccountType
constructor(user: User) {
this.user = new UserIdentifier(user.gradidoID, user.communityUuid)
this.createdAt = user.createdAt.toISOString()
this.accountType = AccountType.COMMUNITY_HUMAN
}
}

View File

@ -1,11 +1,17 @@
export class UserIdentifier {
uuid: string
communityUuid: string
accountNr?: number
import { CommunityUser } from './CommunityUser'
import { IdentifierSeed } from './IdentifierSeed'
constructor(uuid: string, communityUuid: string, accountNr?: number) {
this.uuid = uuid
export class UserIdentifier {
communityUuid: string
communityUser?: CommunityUser
seed?: IdentifierSeed // used for deferred transfers
constructor(communityUuid: string, input: CommunityUser | IdentifierSeed) {
if (input instanceof CommunityUser) {
this.communityUser = input
} else if (input instanceof IdentifierSeed) {
this.seed = input
}
this.communityUuid = communityUuid
this.accountNr = accountNr
}
}

View File

@ -0,0 +1,176 @@
/* eslint-disable no-use-before-define */
import { Decimal } from 'decimal.js-light'
import {
BaseEntity,
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
ManyToOne,
} from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
import { Contribution } from '../Contribution'
import { DltTransaction } from '../DltTransaction'
import { TransactionLink } from '../TransactionLink'
@Entity('transactions')
export class Transaction extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ type: 'int', unsigned: true, unique: true, nullable: true, default: null })
previous: number | null
@Column({ name: 'type_id', unsigned: true, nullable: false })
typeId: number
@Column({
name: 'transaction_link_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
transactionLinkId?: number | null
@Column({
type: 'decimal',
precision: 40,
scale: 20,
nullable: false,
transformer: DecimalTransformer,
})
amount: Decimal
@Column({
type: 'decimal',
precision: 40,
scale: 20,
nullable: false,
transformer: DecimalTransformer,
})
balance: Decimal
@Column({
name: 'balance_date',
type: 'datetime',
default: () => 'CURRENT_TIMESTAMP',
nullable: false,
})
balanceDate: Date
@Column({
type: 'decimal',
precision: 40,
scale: 20,
nullable: false,
transformer: DecimalTransformer,
})
decay: Decimal
@Column({
name: 'decay_start',
type: 'datetime',
nullable: true,
default: null,
})
decayStart: Date | null
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
memo: string
@Column({ name: 'creation_date', type: 'datetime', nullable: true, default: null })
creationDate: Date | null
@Column({ name: 'user_id', unsigned: true, nullable: false })
userId: number
@Column({
name: 'user_community_uuid',
type: 'varchar',
length: 36,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
userCommunityUuid: string | null
@Column({
name: 'user_gradido_id',
type: 'varchar',
length: 36,
nullable: false,
collation: 'utf8mb4_unicode_ci',
})
userGradidoID: string
@Column({
name: 'user_name',
type: 'varchar',
length: 512,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
userName: string | null
@Column({
name: 'linked_user_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
linkedUserId?: number | null
@Column({
name: 'linked_user_community_uuid',
type: 'varchar',
length: 36,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
linkedUserCommunityUuid: string | null
@Column({
name: 'linked_user_gradido_id',
type: 'varchar',
length: 36,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
linkedUserGradidoID: string | null
@Column({
name: 'linked_user_name',
type: 'varchar',
length: 512,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
linkedUserName: string | null
@Column({
name: 'linked_transaction_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
linkedTransactionId?: number | null
@OneToOne(() => Contribution, (contribution) => contribution.transaction)
@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
@ManyToOne(() => TransactionLink, (transactionLink) => transactionLink.transactions)
@JoinColumn({ name: 'transactionLinkId' })
transactionLink?: TransactionLink | null
}

View File

@ -7,10 +7,12 @@ import {
DeleteDateColumn,
OneToOne,
JoinColumn,
OneToMany,
} from 'typeorm'
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
import { DltTransaction } from '../DltTransaction'
import { User } from '../User'
import { Transaction } from '../Transaction'
@Entity('transaction_links')
export class TransactionLink extends BaseEntity {
@ -76,4 +78,8 @@ export class TransactionLink extends BaseEntity {
@OneToOne(() => User, (user) => user.transactionLink)
@JoinColumn({ name: 'userId' })
user: User
@OneToMany(() => Transaction, (transaction) => transaction.transactionLink)
@JoinColumn({ referencedColumnName: 'transactionLinkId' })
transactions: Transaction[]
}

View File

@ -1 +1 @@
export { Transaction } from './0072-add_communityuuid_to_transactions_table/Transaction'
export { Transaction } from './0088-merge_dlt_tables/Transaction'

View File

@ -0,0 +1,35 @@
/* eslint-disable no-unused-vars */
import { TransactionLink } from '../entity/TransactionLink'
import { AbstractLoggingView } from './AbstractLogging.view'
import { DltTransactionLoggingView } from './DltTransactionLogging.view'
import { TransactionLoggingView } from './TransactionLogging.view'
import { UserLoggingView } from './UserLogging.view'
export class TransactionLinkLoggingView extends AbstractLoggingView {
public constructor(private self: TransactionLink) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
id: this.self.id,
userId: this.self.userId,
amount: this.decimalToString(this.self.amount),
holdAvailableAmount: this.decimalToString(this.self.holdAvailableAmount),
memoLength: this.self.memo.length,
createdAt: this.dateToString(this.self.createdAt),
deletedAt: this.dateToString(this.self.deletedAt),
validUntil: this.dateToString(this.self.validUntil),
redeemedAt: this.dateToString(this.self.redeemedAt),
redeemedBy: this.self.redeemedBy,
dltTransaction: this.self.dltTransaction
? new DltTransactionLoggingView(this.self.dltTransaction).toJSON()
: undefined,
user: this.self.user ? new UserLoggingView(this.self.user).toJSON() : undefined,
transactions: this.self.transactions.forEach(
(transaction) => new TransactionLoggingView(transaction),
),
}
}
}

View File

@ -1,8 +1,10 @@
/* eslint-disable no-unused-vars */
import { LargeNumberLike } from 'crypto'
import { Transaction } from '../entity/Transaction'
import { AbstractLoggingView } from './AbstractLogging.view'
import { ContributionLoggingView } from './ContributionLogging.view'
import { DltTransactionLoggingView } from './DltTransactionLogging.view'
import { TransactionLinkLoggingView } from './TransactionLinkLogging.view'
// TODO: move enum into database, maybe rename database
enum TransactionTypeId {
@ -43,7 +45,7 @@ export class TransactionLoggingView extends AbstractLoggingView {
linkedUserName: this.self.linkedUserName?.substring(0, 3) + '...',
linkedTransactionId: this.self.linkedTransactionId,
contribution: this.self.contribution
? new ContributionLoggingView(this.self.contribution)
? new ContributionLoggingView(this.self.contribution).toJSON()
: undefined,
dltTransaction: this.self.dltTransaction
? new DltTransactionLoggingView(this.self.dltTransaction).toJSON()
@ -51,6 +53,9 @@ export class TransactionLoggingView extends AbstractLoggingView {
previousTransaction: this.self.previousTransaction
? new TransactionLoggingView(this.self.previousTransaction).toJSON()
: undefined,
transactionLink: this.self.transactionLink
? new TransactionLinkLoggingView(this.self.transactionLink).toJSON()
: undefined,
}
}
}

View File

@ -3,12 +3,12 @@ import { registerEnumType } from 'type-graphql'
// enum for graphql but with int because it is the same in backend
// for transaction type from backend
export enum InputTransactionType {
CREATION = 1,
SEND = 2,
RECEIVE = 3,
// This is a virtual property, never occurring on the database
DECAY = 4,
LINK_SUMMARY = 5,
GRADIDO_TRANSFER = 'GRADIDO_TRANSFER',
GRADIDO_CREATION = 'GRADIDO_CREATION',
GROUP_FRIENDS_UPDATE = 'GROUP_FRIENDS_UPDATE',
REGISTER_ADDRESS = 'REGISTER_ADDRESS',
GRADIDO_DEFERRED_TRANSFER = 'GRADIDO_DEFERRED_TRANSFER',
COMMUNITY_ROOT = 'COMMUNITY_ROOT',
}
registerEnumType(InputTransactionType, {

View File

@ -1,10 +1,9 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { isValidDateString } from '@validator/DateString'
import { IsBoolean, IsUUID } from 'class-validator'
import { Field, InputType } from 'type-graphql'
import { isValidDateString } from '@validator/DateString'
@InputType()
export class CommunityDraft {
@Field(() => String)

View File

@ -0,0 +1,15 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsPositive, IsUUID } from 'class-validator'
import { Field, Int, InputType } from 'type-graphql'
@InputType()
export class CommunityUser {
@Field(() => String)
@IsUUID('4')
uuid: string
@Field(() => Int, { defaultValue: 1, nullable: true })
@IsPositive()
accountNr?: number
}

View File

@ -4,6 +4,8 @@ import { isValidDateString, isValidNumberString } from '@validator/DateString'
import { IsEnum, IsObject, ValidateNested } from 'class-validator'
import { InputType, Field } from 'type-graphql'
import { AccountType } from '@/graphql/enum/AccountType'
import { UserIdentifier } from './UserIdentifier'
@InputType()
@ -13,14 +15,16 @@ export class TransactionDraft {
@ValidateNested()
user: UserIdentifier
@Field(() => UserIdentifier)
// not used for simply register address
@Field(() => UserIdentifier, { nullable: true })
@IsObject()
@ValidateNested()
linkedUser: UserIdentifier
linkedUser?: UserIdentifier
@Field(() => String)
// not used for register address
@Field(() => String, { nullable: true })
@isValidNumberString()
amount: string
amount?: string
@Field(() => InputTransactionType)
@IsEnum(InputTransactionType)
@ -34,4 +38,14 @@ export class TransactionDraft {
@Field(() => String, { nullable: true })
@isValidDateString()
targetDate?: string
// only for deferred transaction
@Field(() => String, { nullable: true })
@isValidDateString()
timeoutDate?: string
// only for register address
@Field(() => AccountType, { nullable: true })
@IsEnum(AccountType)
accountType?: AccountType
}

View File

@ -1,30 +0,0 @@
// 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,26 +0,0 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsEnum, IsObject, ValidateNested } from 'class-validator'
import { InputType, Field } from 'type-graphql'
import { isValidDateString } from '@validator/DateString'
import { AccountType } from '@/graphql/enum/AccountType'
import { UserIdentifier } from './UserIdentifier'
@InputType()
export class UserAccountDraft {
@Field(() => UserIdentifier)
@IsObject()
@ValidateNested()
user: UserIdentifier
@Field(() => String)
@isValidDateString()
createdAt: string
@Field(() => AccountType)
@IsEnum(AccountType)
accountType: AccountType
}

View File

@ -1,19 +1,24 @@
// https://www.npmjs.com/package/@apollo/protobufjs
import { IsPositive, IsUUID } from 'class-validator'
import { Field, Int, InputType } from 'type-graphql'
import { IsObject, IsUUID, ValidateNested } from 'class-validator'
import { Field, InputType } from 'type-graphql'
import { CommunityUser } from './CommunityUser'
import { IdentifierSeed } from './IdentifierSeed'
@InputType()
export class UserIdentifier {
@Field(() => String)
@IsUUID('4')
uuid: string
@Field(() => String)
@IsUUID('4')
communityUuid: string
@Field(() => Int, { defaultValue: 1, nullable: true })
@IsPositive()
accountNr?: number
@Field(() => CommunityUser, { nullable: true })
@IsObject()
@ValidateNested()
communityUser?: CommunityUser
@Field(() => IdentifierSeed, { nullable: true })
@IsObject()
@ValidateNested()
seed?: IdentifierSeed
}

View File

@ -1,15 +1,14 @@
/* eslint-disable camelcase */
import { AddressType_NONE } from 'gradido-blockchain-js'
import { Arg, Mutation, Query, Resolver } from 'type-graphql'
import { Arg, Query, Resolver } from 'type-graphql'
import { getAddressType } from '@/client/GradidoNode'
import { KeyPairCalculation } from '@/interactions/keyPairCalculation/KeyPairCalculation.context'
import { SendToIotaContext } from '@/interactions/sendToIota/SendToIota.context'
import { logger } from '@/logging/logger'
import { KeyPairCacheManager } from '@/manager/KeyPairCacheManager'
import { uuid4ToHash } from '@/utils/typeConverter'
import { TransactionErrorType } from '../enum/TransactionErrorType'
import { UserAccountDraft } from '../input/UserAccountDraft'
import { UserIdentifier } from '../input/UserIdentifier'
import { TransactionError } from '../model/TransactionError'
import { TransactionResult } from '../model/TransactionResult'
@ -25,6 +24,7 @@ export class AccountResolver {
new TransactionError(TransactionErrorType.NOT_FOUND, 'cannot get user public key'),
)
}
// ask gradido node server for account type, if type !== NONE account exist
const addressType = await getAddressType(
publicKey.data(),
@ -33,21 +33,4 @@ export class AccountResolver {
logger.info('isAccountExist', userIdentifier)
return addressType !== AddressType_NONE
}
@Mutation(() => TransactionResult)
async registerAddress(
@Arg('data')
userAccountDraft: UserAccountDraft,
): Promise<TransactionResult> {
try {
return await SendToIotaContext(userAccountDraft)
} catch (err) {
if (err instanceof TransactionError) {
return new TransactionResult(err)
} else {
logger.error('error in register address: ', err)
throw err
}
}
}
}

View File

@ -1,4 +1,3 @@
import { TransactionLinkDraft } from '@input/TransactionLinkDraft'
import { Resolver, Arg, Mutation } from 'type-graphql'
import { SendToIotaContext } from '@/interactions/sendToIota/SendToIota.context'
@ -25,21 +24,4 @@ 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

@ -15,9 +15,7 @@ import { UserKeyPairRole } from './UserKeyPair.role'
* @DCI-Context
* Context for calculating key pair for signing transactions
*/
export async function KeyPairCalculation(
input: UserIdentifier | string | IdentifierSeed,
): Promise<KeyPairEd25519> {
export async function KeyPairCalculation(input: UserIdentifier | string): Promise<KeyPairEd25519> {
const cache = KeyPairCacheManager.getInstance()
// Try cache lookup first
@ -26,11 +24,9 @@ export async function KeyPairCalculation(
return keyPair
}
const retrieveKeyPair = async (
input: UserIdentifier | string | IdentifierSeed,
): Promise<KeyPairEd25519> => {
if (input instanceof IdentifierSeed) {
return new LinkedTransactionKeyPairRole(input.seed).generateKeyPair()
const retrieveKeyPair = async (input: UserIdentifier | string): Promise<KeyPairEd25519> => {
if (input instanceof UserIdentifier && input.seed) {
return new LinkedTransactionKeyPairRole(input.seed.seed).generateKeyPair()
}
const communityUUID = input instanceof UserIdentifier ? input.communityUuid : input
@ -51,7 +47,7 @@ export async function KeyPairCalculation(
}
if (input instanceof UserIdentifier) {
const userKeyPair = new UserKeyPairRole(input, communityKeyPair).generateKeyPair()
const accountNr = input.accountNr ?? 1
const accountNr = input.communityUser?.accountNr ?? 1
return new AccountKeyPairRole(accountNr, userKeyPair).generateKeyPair()
}
return communityKeyPair

View File

@ -13,7 +13,11 @@ export class RemoteAccountKeyPairRole extends AbstractRemoteKeyPairRole {
}
public async retrieveKeyPair(): Promise<KeyPairEd25519> {
const nameHash = uuid4ToHash(this.user.uuid)
if (!this.user.communityUser) {
throw new LogError('missing community user')
}
const nameHash = uuid4ToHash(this.user.communityUser.uuid)
const confirmedTransactions = await getTransactions(
0,
30,

View File

@ -1,6 +1,7 @@
import { KeyPairEd25519 } from 'gradido-blockchain-js'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { LogError } from '@/server/LogError'
import { hardenDerivationIndex } from '@/utils/derivationHelper'
import { uuid4ToBuffer } from '@/utils/typeConverter'
@ -14,7 +15,10 @@ export class UserKeyPairRole extends AbstractKeyPairRole {
public generateKeyPair(): KeyPairEd25519 {
// example gradido id: 03857ac1-9cc2-483e-8a91-e5b10f5b8d16 =>
// wholeHex: '03857ac19cc2483e8a91e5b10f5b8d16']
const wholeHex = uuid4ToBuffer(this.user.uuid)
if (!this.user.communityUser) {
throw new LogError('missing community user')
}
const wholeHex = uuid4ToBuffer(this.user.communityUser.uuid)
const parts = []
for (let i = 0; i < 4; i++) {
parts[i] = hardenDerivationIndex(wholeHex.subarray(i * 4, (i + 1) * 4).readUInt32BE())

View File

@ -1,7 +1,8 @@
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { LogError } from '@/server/LogError'
import { TransactionError } from '@/graphql/model/TransactionError'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
@ -17,12 +18,27 @@ export class CreationTransactionRole extends AbstractTransactionRole {
}
getRecipientCommunityUuid(): string {
throw new LogError('cannot be used as cross group transaction')
throw new TransactionError(
TransactionErrorType.LOGIC_ERROR,
'creation: cannot be used as cross group transaction',
)
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.targetDate) {
throw new LogError('target date missing for creation transaction')
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'creation: target date missing',
)
}
if (!this.self.linkedUser) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'creation: linked user missing',
)
}
if (!this.self.amount) {
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'creation: amount missing')
}
const builder = new GradidoTransactionBuilder()
const recipientKeyPair = await KeyPairCalculation(this.self.user)

View File

@ -1,15 +1,15 @@
import { GradidoTransactionBuilder, GradidoTransfer, TransferAmount } from 'gradido-blockchain-js'
import { IdentifierSeed } from '@/graphql/input/IdentifierSeed'
import { TransactionLinkDraft } from '@/graphql/input/TransactionLinkDraft'
import { LogError } from '@/server/LogError'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { TransactionError } from '@/graphql/model/TransactionError'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
import { AbstractTransactionRole } from './AbstractTransaction.role'
export class DeferredTransferTransactionRole extends AbstractTransactionRole {
constructor(protected self: TransactionLinkDraft) {
constructor(protected self: TransactionDraft) {
super()
}
@ -18,13 +18,34 @@ export class DeferredTransferTransactionRole extends AbstractTransactionRole {
}
getRecipientCommunityUuid(): string {
throw new LogError('cannot be used as cross group transaction')
throw new TransactionError(
TransactionErrorType.LOGIC_ERROR,
'deferred transfer: cannot be used as cross group transaction',
)
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.linkedUser || !this.self.linkedUser.seed) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: missing linked user or not a seed',
)
}
if (!this.self.amount) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: amount missing',
)
}
if (!this.self.timeoutDate) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'deferred transfer: timeout date missing',
)
}
const builder = new GradidoTransactionBuilder()
const senderKeyPair = await KeyPairCalculation(this.self.user)
const recipientKeyPair = await KeyPairCalculation(new IdentifierSeed(this.self.seed))
const recipientKeyPair = await KeyPairCalculation(this.self.linkedUser)
builder
.setCreatedAt(new Date(this.self.createdAt))

View File

@ -1,7 +1,9 @@
/* eslint-disable camelcase */
import { GradidoTransactionBuilder } from 'gradido-blockchain-js'
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { TransactionError } from '@/graphql/model/TransactionError'
import { LogError } from '@/server/LogError'
import { accountTypeToAddressType, uuid4ToHash } from '@/utils/typeConverter'
@ -10,7 +12,7 @@ import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.con
import { AbstractTransactionRole } from './AbstractTransaction.role'
export class RegisterAddressTransactionRole extends AbstractTransactionRole {
constructor(private self: UserAccountDraft) {
constructor(private self: TransactionDraft) {
super()
}
@ -23,6 +25,20 @@ export class RegisterAddressTransactionRole extends AbstractTransactionRole {
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.accountType) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'register address: account type missing',
)
}
if (!this.self.user.communityUser) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
"register address: user isn't a community user",
)
}
const builder = new GradidoTransactionBuilder()
const communityKeyPair = await KeyPairCalculation(this.self.user.communityUuid)
const accountKeyPair = await KeyPairCalculation(this.self.user)
@ -31,7 +47,7 @@ export class RegisterAddressTransactionRole extends AbstractTransactionRole {
.setRegisterAddress(
accountKeyPair.getPublicKey(),
accountTypeToAddressType(this.self.accountType),
uuid4ToHash(this.self.user.uuid),
uuid4ToHash(this.self.user.communityUser.uuid),
)
.sign(communityKeyPair)
.sign(accountKeyPair)

View File

@ -12,8 +12,6 @@ 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'
import { TransactionResult } from '@/graphql/model/TransactionResult'
@ -34,7 +32,7 @@ import { TransferTransactionRole } from './TransferTransaction.role'
* send every transaction only once to iota!
*/
export async function SendToIotaContext(
input: TransactionDraft | UserAccountDraft | CommunityDraft | TransactionLinkDraft,
input: TransactionDraft | CommunityDraft,
): Promise<TransactionResult> {
const validate = (transaction: GradidoTransaction): void => {
try {
@ -76,17 +74,25 @@ export async function SendToIotaContext(
let role: AbstractTransactionRole
if (input instanceof TransactionDraft) {
if (input.type === InputTransactionType.CREATION) {
role = new CreationTransactionRole(input)
} else if (input.type === InputTransactionType.SEND) {
role = new TransferTransactionRole(input)
} else {
throw new LogError('not supported transaction type')
switch (input.type) {
case InputTransactionType.GRADIDO_CREATION:
role = new CreationTransactionRole(input)
break
case InputTransactionType.GRADIDO_TRANSFER:
role = new TransferTransactionRole(input)
break
case InputTransactionType.REGISTER_ADDRESS:
role = new RegisterAddressTransactionRole(input)
break
case InputTransactionType.GRADIDO_DEFERRED_TRANSFER:
role = new DeferredTransferTransactionRole(input)
break
default:
throw new TransactionError(
TransactionErrorType.NOT_IMPLEMENTED_YET,
'not supported transaction type: ' + input.type,
)
}
} else if (input instanceof TransactionLinkDraft) {
role = new DeferredTransferTransactionRole(input)
} else if (input instanceof UserAccountDraft) {
role = new RegisterAddressTransactionRole(input)
} else if (input instanceof CommunityDraft) {
role = new CommunityRootTransactionRole(input)
} else {

View File

@ -1,6 +1,9 @@
import { GradidoTransactionBuilder, TransferAmount } from 'gradido-blockchain-js'
import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType'
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { TransactionError } from '@/graphql/model/TransactionError'
import { uuid4ToHash } from '@/utils/typeConverter'
import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.context'
@ -8,8 +11,16 @@ import { KeyPairCalculation } from '../keyPairCalculation/KeyPairCalculation.con
import { AbstractTransactionRole } from './AbstractTransaction.role'
export class TransferTransactionRole extends AbstractTransactionRole {
constructor(protected self: TransactionDraft) {
private linkedUser: UserIdentifier
constructor(private self: TransactionDraft) {
super()
if (!this.self.linkedUser) {
throw new TransactionError(
TransactionErrorType.MISSING_PARAMETER,
'transfer: linked user missing',
)
}
this.linkedUser = this.self.linkedUser
}
getSenderCommunityUuid(): string {
@ -17,13 +28,16 @@ export class TransferTransactionRole extends AbstractTransactionRole {
}
getRecipientCommunityUuid(): string {
return this.self.linkedUser.communityUuid
return this.linkedUser.communityUuid
}
public async getGradidoTransactionBuilder(): Promise<GradidoTransactionBuilder> {
if (!this.self.amount) {
throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'transfer: amount missing')
}
const builder = new GradidoTransactionBuilder()
const senderKeyPair = await KeyPairCalculation(this.self.user)
const recipientKeyPair = await KeyPairCalculation(this.self.linkedUser)
const recipientKeyPair = await KeyPairCalculation(this.linkedUser)
builder
.setCreatedAt(new Date(this.self.createdAt))
.setMemo('dummy memo for transfer')
@ -32,7 +46,7 @@ export class TransferTransactionRole extends AbstractTransactionRole {
recipientKeyPair.getPublicKey(),
)
const senderCommunity = this.self.user.communityUuid
const recipientCommunity = this.self.linkedUser.communityUuid
const recipientCommunity = this.linkedUser.communityUuid
if (senderCommunity !== recipientCommunity) {
// we have a cross group transaction
builder

View File

@ -0,0 +1,17 @@
import { CommunityUser } from '@/graphql/input/CommunityUser'
import { AbstractLoggingView } from './AbstractLogging.view'
export class CommunityUserLoggingView extends AbstractLoggingView {
public constructor(private self: CommunityUser) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
uuid: this.self.uuid,
accountNr: this.self.accountNr,
}
}
}

View File

@ -0,0 +1,16 @@
import { IdentifierSeed } from '@/graphql/input/IdentifierSeed'
import { AbstractLoggingView } from './AbstractLogging.view'
export class IdentifierSeedLoggingView extends AbstractLoggingView {
public constructor(private self: IdentifierSeed) {
super()
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
seed: this.self.seed,
}
}
}

View File

@ -1,6 +1,8 @@
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
import { AbstractLoggingView } from './AbstractLogging.view'
import { CommunityUserLoggingView } from './CommunityUserLogging.view'
import { IdentifierSeedLoggingView } from './IdentifierSeedLogging.view'
export class UserIdentifierLoggingView extends AbstractLoggingView {
public constructor(private self: UserIdentifier) {
@ -10,9 +12,11 @@ export class UserIdentifierLoggingView extends AbstractLoggingView {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public toJSON(): any {
return {
uuid: this.self.uuid,
communityUuid: this.self.communityUuid,
accountNr: this.self.accountNr,
communityUser: this.self.communityUser
? new CommunityUserLoggingView(this.self.communityUser).toJSON()
: undefined,
seed: this.self.seed ? new IdentifierSeedLoggingView(this.self.seed).toJSON() : undefined,
}
}
}

View File

@ -45,14 +45,11 @@ export class KeyPairCacheManager {
return this.homeCommunityUUID
}
public findKeyPair(input: UserIdentifier | string | IdentifierSeed): KeyPairEd25519 | undefined {
public findKeyPair(input: UserIdentifier | string): KeyPairEd25519 | undefined {
return this.cache.get(this.getKey(input))
}
public addKeyPair(
input: UserIdentifier | string | IdentifierSeed,
keyPair: KeyPairEd25519,
): void {
public addKeyPair(input: UserIdentifier | string, keyPair: KeyPairEd25519): void {
const key = this.getKey(input)
if (this.cache.has(key)) {
throw new LogError('key already exist, cannot add', key)
@ -60,13 +57,17 @@ export class KeyPairCacheManager {
this.cache.set(key, keyPair)
}
protected getKey(input: UserIdentifier | string | IdentifierSeed): string {
protected getKey(input: UserIdentifier | string): string {
if (input instanceof UserIdentifier) {
return input.uuid
} else if (input instanceof IdentifierSeed) {
return input.seed
} else {
if (input.communityUser) {
return input.communityUser.uuid
} else if (input.seed) {
return input.seed.seed
}
throw new LogError('unhandled branch')
} else if (typeof input === 'string') {
return input
}
throw new LogError('unhandled input type')
}
}