From 8ccc52ae0a4eb83943512e1b776cd25135219177 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Tue, 24 Oct 2023 19:48:01 +0200 Subject: [PATCH] refactored, not final yet --- dlt-connector/.eslintrc.js | 2 + dlt-connector/package.json | 1 + dlt-connector/src/data/Account.factory.ts | 44 ++++++++++++++++ dlt-connector/src/data/account.factory.ts | 42 --------------- dlt-connector/src/data/community.factory.ts | 31 ----------- .../src/graphql/resolver/CommunityResolver.ts | 18 ++++++- .../community/AddCommunity.context.ts | 30 +++++++++++ .../backendToDb/community/Community.role.ts | 23 ++++++++- .../community/ForeignCommunity.role.ts | 19 +------ .../community/HomeCommunity.role.ts | 51 +++++++++++++------ .../community/community.context.ts | 30 ----------- .../CommunityRootTransaction.role.ts | 2 +- .../CreateTransationRecipe.context.ts | 51 +++++++++++++++++++ .../transaction/TransactionRecipe.role.ts | 6 ++- .../transaction/transaction.context.ts | 20 -------- dlt-connector/yarn.lock | 5 ++ 16 files changed, 214 insertions(+), 161 deletions(-) create mode 100644 dlt-connector/src/data/Account.factory.ts delete mode 100644 dlt-connector/src/data/account.factory.ts delete mode 100644 dlt-connector/src/data/community.factory.ts create mode 100644 dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts delete mode 100644 dlt-connector/src/interactions/backendToDb/community/community.context.ts create mode 100644 dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts delete mode 100644 dlt-connector/src/interactions/backendToDb/transaction/transaction.context.ts diff --git a/dlt-connector/.eslintrc.js b/dlt-connector/.eslintrc.js index c477a4cb1..b2bc4047e 100644 --- a/dlt-connector/.eslintrc.js +++ b/dlt-connector/.eslintrc.js @@ -14,6 +14,7 @@ module.exports = { // 'plugin:import/typescript', // 'plugin:security/recommended', 'plugin:@eslint-community/eslint-comments/recommended', + 'plugin:dci-lint/recommended', ], settings: { 'import/parsers': { @@ -36,6 +37,7 @@ module.exports = { htmlWhitespaceSensitivity: 'ignore', }, ], + // 'dci-lint/literal-role-contracts': 'off' // import // 'import/export': 'error', // 'import/no-deprecated': 'error', diff --git a/dlt-connector/package.json b/dlt-connector/package.json index d64b6d743..4f9c5a26a 100644 --- a/dlt-connector/package.json +++ b/dlt-connector/package.json @@ -53,6 +53,7 @@ "eslint-config-prettier": "^8.8.0", "eslint-config-standard": "^17.0.0", "eslint-import-resolver-typescript": "^3.5.4", + "eslint-plugin-dci-lint": "^0.3.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-jest": "^27.2.1", "eslint-plugin-n": "^15.7.0", diff --git a/dlt-connector/src/data/Account.factory.ts b/dlt-connector/src/data/Account.factory.ts new file mode 100644 index 000000000..c3ad02fa7 --- /dev/null +++ b/dlt-connector/src/data/Account.factory.ts @@ -0,0 +1,44 @@ +import { KeyManager } from '@/controller/KeyManager' +import { KeyPair } from '@/data/KeyPair' +import { AddressType } from '@/data/proto/3_3/enum/AddressType' +import { hardenDerivationIndex } from '@/utils/derivationHelper' +import { Account } from '@entity/Account' +import Decimal from 'decimal.js-light' + +const GMW_ACCOUNT_DERIVATION_INDEX = 1 +const AUF_ACCOUNT_DERIVATION_INDEX = 2 + +export class AccountFactory { + public static createAccount( + keyPair: KeyPair, + createdAt: Date, + derivationIndex: number, + type: AddressType, + ): Account { + const account = Account.create() + account.derivationIndex = derivationIndex + account.derive2Pubkey = KeyManager.getInstance().derive([derivationIndex], keyPair).publicKey + account.type = type.valueOf() + account.createdAt = createdAt + account.balance = new Decimal(0) + return account + } + + public static createGmwAccount(keyPair: KeyPair, createdAt: Date): Account { + return AccountFactory.createAccount( + keyPair, + createdAt, + hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX), + AddressType.COMMUNITY_GMW, + ) + } + + public static createAufAccount(keyPair: KeyPair, createdAt: Date): Account { + return AccountFactory.createAccount( + keyPair, + createdAt, + hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX), + AddressType.COMMUNITY_AUF, + ) + } +} diff --git a/dlt-connector/src/data/account.factory.ts b/dlt-connector/src/data/account.factory.ts deleted file mode 100644 index d823c8345..000000000 --- a/dlt-connector/src/data/account.factory.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { KeyManager } from '@/controller/KeyManager' -import { KeyPair } from '@/data/KeyPair' -import { AddressType } from '@/data/proto/3_3/enum/AddressType' -import { hardenDerivationIndex } from '@/utils/derivationHelper' -import { Account } from '@entity/Account' -import Decimal from 'decimal.js-light' - -const GMW_ACCOUNT_DERIVATION_INDEX = 1 -const AUF_ACCOUNT_DERIVATION_INDEX = 2 - -export const createAccount = ( - keyPair: KeyPair, - createdAt: Date, - derivationIndex: number, - type: AddressType, -): Account => { - const account = Account.create() - account.derivationIndex = derivationIndex - account.derive2Pubkey = KeyManager.getInstance().derive([derivationIndex], keyPair).publicKey - account.type = type.valueOf() - account.createdAt = createdAt - account.balance = new Decimal(0) - return account -} - -export const createGmwAccount = (keyPair: KeyPair, createdAt: Date): Account => { - return createAccount( - keyPair, - createdAt, - hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX), - AddressType.COMMUNITY_GMW, - ) -} - -export const createAufAccount = (keyPair: KeyPair, createdAt: Date): Account => { - return createAccount( - keyPair, - createdAt, - hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX), - AddressType.COMMUNITY_AUF, - ) -} diff --git a/dlt-connector/src/data/community.factory.ts b/dlt-connector/src/data/community.factory.ts deleted file mode 100644 index d80ff315a..000000000 --- a/dlt-connector/src/data/community.factory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { KeyManager } from '@/controller/KeyManager' -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' -import { Community } from '@entity/Community' -import { createAufAccount, createGmwAccount } from './account.factory' - -export const createCommunity = (communityDraft: CommunityDraft, topic?: string): Community => { - const communityEntity = Community.create() - communityEntity.iotaTopic = topic ?? iotaTopicFromCommunityUUID(communityDraft.uuid) - communityEntity.createdAt = new Date(communityDraft.createdAt) - communityEntity.foreign = communityDraft.foreign - return communityEntity -} - -export const createHomeCommunity = (communityDraft: CommunityDraft, topic?: string): Community => { - // create community entity - const community = createCommunity(communityDraft, topic) - - // generate key pair for signing transactions and deriving all keys for community - const keyPair = KeyManager.generateKeyPair() - community.rootPubkey = keyPair.publicKey - community.rootPrivkey = keyPair.privateKey - community.rootChaincode = keyPair.chainCode - // we should only have one home community per server - KeyManager.getInstance().setHomeCommunityKeyPair(keyPair) - - // create auf account and gmw account - community.aufAccount = createAufAccount(keyPair, community.createdAt) - community.gmwAccount = createGmwAccount(keyPair, community.createdAt) - return community -} diff --git a/dlt-connector/src/graphql/resolver/CommunityResolver.ts b/dlt-connector/src/graphql/resolver/CommunityResolver.ts index 43a871dd8..2259f604c 100644 --- a/dlt-connector/src/graphql/resolver/CommunityResolver.ts +++ b/dlt-connector/src/graphql/resolver/CommunityResolver.ts @@ -11,7 +11,7 @@ import { CommunityArg } from '@arg/CommunityArg' import { LogError } from '@/server/LogError' import { logger } from '@/server/logger' import { CommunityRepository } from '@/data/Community.repository' -import { addCommunity } from '@/interactions/backendToDb/community/community.context' +import { AddCommunityContext } from '@/interactions/backendToDb/community/AddCommunity.context' @Resolver() export class CommunityResolver { @@ -54,6 +54,20 @@ export class CommunityResolver { new TransactionError(TransactionErrorType.ALREADY_EXIST, 'community already exist!'), ) } - return await addCommunity(communityDraft, topic) + // prepare context for interaction + // shouldn't throw at all + // TODO: write tests to make sure that it doesn't throw + const addCommunityContext = new AddCommunityContext(communityDraft, topic) + try { + // actually run interaction, create community, accounts for foreign community and transactionRecipe + await addCommunityContext.run() + return new TransactionResult() + } catch (error) { + if (error instanceof TransactionError) { + return new TransactionResult(error) + } else { + throw error + } + } } } diff --git a/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts b/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts new file mode 100644 index 000000000..f2518d45a --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/community/AddCommunity.context.ts @@ -0,0 +1,30 @@ +import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { ForeignCommunityRole } from './ForeignCommunity.role' +import { HomeCommunityRole } from './HomeCommunity.role' +import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' +import { CommunityRole } from './Community.role' + +/** + * @DCI-Context + * Context for adding community to DB + * using roles to distinct between foreign and home communities + */ +export class AddCommunityContext { + private communityRole: CommunityRole + private iotaTopic: string + public constructor(private communityDraft: CommunityDraft, iotaTopic?: string) { + if (!iotaTopic) { + this.iotaTopic = iotaTopicFromCommunityUUID(this.communityDraft.uuid) + } else { + this.iotaTopic = iotaTopic + } + this.communityRole = communityDraft.foreign + ? new ForeignCommunityRole() + : new HomeCommunityRole() + } + + public async run(): Promise { + this.communityRole.create(this.communityDraft, this.iotaTopic) + await this.communityRole.store() + } +} diff --git a/dlt-connector/src/interactions/backendToDb/community/Community.role.ts b/dlt-connector/src/interactions/backendToDb/community/Community.role.ts index 144d3bc5c..f2fa7d0ac 100644 --- a/dlt-connector/src/interactions/backendToDb/community/Community.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/Community.role.ts @@ -1,6 +1,27 @@ +import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { TransactionError } from '@/graphql/model/TransactionError' +import { logger } from '@/server/logger' import { Community } from '@entity/Community' export abstract class CommunityRole { - abstract addCommunity(communityDraft: CommunityDraft, topic: string): Promise + protected self: Community + public constructor() { + this.self = Community.create() + } + + public create(communityDraft: CommunityDraft, topic: string): void { + this.self.iotaTopic = topic + this.self.createdAt = new Date(communityDraft.createdAt) + this.self.foreign = communityDraft.foreign + } + + public store(): Promise { + try { + return this.self.save() + } catch (error) { + logger.error('error saving new community into db: %s', error) + throw new TransactionError(TransactionErrorType.DB_ERROR, 'error saving community into db') + } + } } diff --git a/dlt-connector/src/interactions/backendToDb/community/ForeignCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/ForeignCommunity.role.ts index 76af3026c..cf93deaa5 100644 --- a/dlt-connector/src/interactions/backendToDb/community/ForeignCommunity.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/ForeignCommunity.role.ts @@ -1,19 +1,4 @@ -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { Community } from '@entity/Community' import { CommunityRole } from './Community.role' -import { logger } from '@/server/logger' -import { TransactionError } from '@/graphql/model/TransactionError' -import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' -import { createCommunity } from '@/data/community.factory' -export class ForeignCommunityRole extends CommunityRole { - addCommunity(communityDraft: CommunityDraft, topic: string): Promise { - const community = createCommunity(communityDraft, topic) - try { - return community.save() - } catch (error) { - logger.error('error saving new foreign community into db: %s', error) - throw new TransactionError(TransactionErrorType.DB_ERROR, 'error saving community into db') - } - } -} +// same as base class +export class ForeignCommunityRole extends CommunityRole {} diff --git a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts index df670398f..4ae946498 100644 --- a/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts +++ b/dlt-connector/src/interactions/backendToDb/community/HomeCommunity.role.ts @@ -1,28 +1,47 @@ import { CommunityDraft } from '@/graphql/input/CommunityDraft' import { Community } from '@entity/Community' import { CommunityRole } from './Community.role' -import { getTransaction } from '@/client/GradidoNode' -import { timestampSecondsToDate } from '@/utils/typeConverter' -import { createHomeCommunity } from '@/data/community.factory' -import { createCommunityRootTransactionRecipe } from '../transaction/transaction.context' import { QueryRunner } from 'typeorm' +import { Transaction } from '@entity/Transaction' +import { KeyManager } from '@/controller/KeyManager' +import { AccountFactory } from '@/data/Account.factory' +import { CreateTransactionRecipeContext } from '../transaction/CreateTransationRecipe.context' export class HomeCommunityRole extends CommunityRole { + private transactionRecipe: Transaction + + public create(communityDraft: CommunityDraft, topic: string): void { + super.create(communityDraft, topic) + // generate key pair for signing transactions and deriving all keys for community + const keyPair = KeyManager.generateKeyPair() + this.self.rootPubkey = keyPair.publicKey + this.self.rootPrivkey = keyPair.privateKey + this.self.rootChaincode = keyPair.chainCode + // we should only have one home community per server + KeyManager.getInstance().setHomeCommunityKeyPair(keyPair) + + // create auf account and gmw account + 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) + transactionRecipeContext.run() + this.transactionRecipe = transactionRecipeContext.getTransactionRecipe() + } + + public store(): Promise { + + } + public async addCommunity(communityDraft: CommunityDraft, topic: string): Promise { const community = createHomeCommunity(communityDraft, topic) - // check if a CommunityRoot Transaction exist already on iota blockchain - const existingCommunityRootTransaction = await getTransaction(1, community.iotaTopic) - if (existingCommunityRootTransaction) { - community.confirmedAt = timestampSecondsToDate(existingCommunityRootTransaction.confirmedAt) - return community.save() - } else { - createCommunityRootTransactionRecipe(communityDraft, community).storeAsTransaction( - async (queryRunner: QueryRunner): Promise => { - await queryRunner.manager.save(community) - }, - ) - } + createCommunityRootTransactionRecipe(communityDraft, community).storeAsTransaction( + async (queryRunner: QueryRunner): Promise => { + await queryRunner.manager.save(community) + }, + ) return community.save() } } diff --git a/dlt-connector/src/interactions/backendToDb/community/community.context.ts b/dlt-connector/src/interactions/backendToDb/community/community.context.ts deleted file mode 100644 index cd55599bf..000000000 --- a/dlt-connector/src/interactions/backendToDb/community/community.context.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { TransactionResult } from '@/graphql/model/TransactionResult' -import { ForeignCommunityRole } from './ForeignCommunity.role' -import { HomeCommunityRole } from './HomeCommunity.role' -import { TransactionError } from '@/graphql/model/TransactionError' -import { TransactionsManager } from '@/controller/TransactionsManager' -import { iotaTopicFromCommunityUUID } from '@/utils/typeConverter' - -export const addCommunity = async ( - communityDraft: CommunityDraft, - iotaTopic?: string, -): Promise => { - const communityRole = communityDraft.foreign - ? new ForeignCommunityRole() - : new HomeCommunityRole() - try { - if (!iotaTopic) { - iotaTopic = iotaTopicFromCommunityUUID(communityDraft.uuid) - } - await communityRole.addCommunity(communityDraft, iotaTopic) - await TransactionsManager.getInstance().addTopic(iotaTopic) - return new TransactionResult() - } catch (error) { - if (error instanceof TransactionError) { - return new TransactionResult(error) - } else { - throw error - } - } -} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts index 3bce63461..aa09f89b2 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/CommunityRootTransaction.role.ts @@ -6,7 +6,7 @@ import { KeyPair } from '@/data/KeyPair' import { sign } from '@/utils/cryptoHelper' export class CommunityRootTransactionRole extends TransactionRecipeRole { - public createFromCommunityDraft( + public createFromCommunityRoot( communityDraft: CommunityDraft, community: Community, ): CommunityRootTransactionRole { diff --git a/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts new file mode 100644 index 000000000..454dd5b41 --- /dev/null +++ b/dlt-connector/src/interactions/backendToDb/transaction/CreateTransationRecipe.context.ts @@ -0,0 +1,51 @@ +import { CommunityDraft } from '@/graphql/input/CommunityDraft' +import { Community } from '@entity/Community' +import { TransactionRecipeRole } from './TransactionRecipe.role' +import { CommunityRootTransactionRole } from './CommunityRootTransaction.role' +import { TransactionDraft } from '@/graphql/input/TransactionDraft' +import { Transaction } from '@entity/Transaction' +import { TransactionError } from '@/graphql/model/TransactionError' +import { TransactionErrorType } from '@/graphql/enum/TransactionErrorType' + +/** + * @DCI-Context + * Context for create and add Transaction Recipe to DB + */ + +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() { + return this.community + } + + public getTransactionRecipe(): Transaction { + return this.transactionRecipeRole.getTransaction() + } + + public run(): void { + if (this.draft instanceof TransactionDraft) { + this.transactionRecipeRole = new TransactionRecipeRole().create(this.draft) + } else if (this.draft instanceof CommunityDraft) { + if (!this.community) { + throw new TransactionError(TransactionErrorType.MISSING_PARAMETER, 'community was not set') + } + this.transactionRecipeRole = new CommunityRootTransactionRole().createFromCommunityRoot( + this.draft, + this.community, + ) + } + } +} diff --git a/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts b/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts index 0ab41f0d4..4a432f67e 100644 --- a/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts +++ b/dlt-connector/src/interactions/backendToDb/transaction/TransactionRecipe.role.ts @@ -14,10 +14,14 @@ export class TransactionRecipeRole { this.transactionBuilder = new TransactionBuilder() } - public createFromTransactionDraft(transactionDraft: TransactionDraft): TransactionRecipeRole { + public create(transactionDraft: TransactionDraft): TransactionRecipeRole { return this } + public getTransaction(): Transaction { + return this.transactionBuilder.getTransaction() + } + public async storeAsTransaction( transactionFunction: (queryRunner: QueryRunner) => Promise, ): Promise { diff --git a/dlt-connector/src/interactions/backendToDb/transaction/transaction.context.ts b/dlt-connector/src/interactions/backendToDb/transaction/transaction.context.ts deleted file mode 100644 index 5c8bda04f..000000000 --- a/dlt-connector/src/interactions/backendToDb/transaction/transaction.context.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { CommunityDraft } from '@/graphql/input/CommunityDraft' -import { Community } from '@entity/Community' -import { TransactionRecipeRole } from './TransactionRecipe.role' -import { CommunityRootTransactionRole } from './CommunityRootTransaction.role' -import { TransactionDraft } from '@/graphql/input/TransactionDraft' - -export const createCommunityRootTransactionRecipe = ( - communityDraft: CommunityDraft, - community: Community, -): TransactionRecipeRole => { - const communityRootTransactionRole = new CommunityRootTransactionRole() - return communityRootTransactionRole.createFromCommunityDraft(communityDraft, community) -} - -export const createTransactionRecipe = ( - transactionDraft: TransactionDraft, -): TransactionRecipeRole => { - const transactionRecipeRole = new TransactionRecipeRole() - return transactionRecipeRole.createFromTransactionDraft(transactionDraft) -} diff --git a/dlt-connector/yarn.lock b/dlt-connector/yarn.lock index e54f2866d..3c7a8bf36 100644 --- a/dlt-connector/yarn.lock +++ b/dlt-connector/yarn.lock @@ -2586,6 +2586,11 @@ eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: dependencies: debug "^3.2.7" +eslint-plugin-dci-lint@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-dci-lint/-/eslint-plugin-dci-lint-0.3.0.tgz#dcd73c50505b589b415017cdb72716f98e9495c3" + integrity sha512-BhgrwJ5k3eMN41NwCZ/tYQGDTMOrHXpH8XOfRZrGtPqmlnOZCVGWow+KyZMz0/wOFVpXx/q9B0y7R7qtU7lnqg== + eslint-plugin-es@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9"