diff options
author | Florian Dold <florian@dold.me> | 2021-10-20 13:06:31 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2021-10-20 13:06:31 +0200 |
commit | 589c2a338284e038cf03e4c8734671c8f9f8ebda (patch) | |
tree | 0f07d709abed8f4a90cf0866ea99756055e80950 /packages/taler-wallet-cli/src/harness/libeufin.ts | |
parent | c3570484a8e2cd342d274e8cdb4ea0fe41c8de50 (diff) | |
download | wallet-core-589c2a338284e038cf03e4c8734671c8f9f8ebda.tar.xz |
wallet-cli: benchmarking
Diffstat (limited to 'packages/taler-wallet-cli/src/harness/libeufin.ts')
-rw-r--r-- | packages/taler-wallet-cli/src/harness/libeufin.ts | 1676 |
1 files changed, 1676 insertions, 0 deletions
diff --git a/packages/taler-wallet-cli/src/harness/libeufin.ts b/packages/taler-wallet-cli/src/harness/libeufin.ts new file mode 100644 index 000000000..11447b389 --- /dev/null +++ b/packages/taler-wallet-cli/src/harness/libeufin.ts @@ -0,0 +1,1676 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ +import axios from "axios"; +import { URL } from "@gnu-taler/taler-util"; +import { getRandomIban, getRandomString } from "../harness/helpers.js"; +import { + GlobalTestState, + DbInfo, + pingProc, + ProcessWrapper, + runCommand, + setupDb, + sh, +} from "../harness/harness.js"; + +export interface LibeufinSandboxServiceInterface { + baseUrl: string; +} + +export interface LibeufinNexusServiceInterface { + baseUrl: string; +} + +export interface LibeufinServices { + libeufinSandbox: LibeufinSandboxService; + libeufinNexus: LibeufinNexusService; + commonDb: DbInfo; +} + +export interface LibeufinSandboxConfig { + httpPort: number; + databaseJdbcUri: string; +} + +export interface LibeufinNexusConfig { + httpPort: number; + databaseJdbcUri: string; +} + +export interface DeleteBankConnectionRequest { + bankConnectionId: string; +} + +interface LibeufinNexusMoneyMovement { + amount: string; + creditDebitIndicator: string; + details: { + debtor: { + name: string; + }; + debtorAccount: { + iban: string; + }; + debtorAgent: { + bic: string; + }; + creditor: { + name: string; + }; + creditorAccount: { + iban: string; + }; + creditorAgent: { + bic: string; + }; + endToEndId: string; + unstructuredRemittanceInformation: string; + }; +} + +interface LibeufinNexusBatches { + batchTransactions: Array<LibeufinNexusMoneyMovement>; +} + +interface LibeufinNexusTransaction { + amount: string; + creditDebitIndicator: string; + status: string; + bankTransactionCode: string; + valueDate: string; + bookingDate: string; + accountServicerRef: string; + batches: Array<LibeufinNexusBatches>; +} + +interface LibeufinNexusTransactions { + transactions: Array<LibeufinNexusTransaction>; +} + +export interface LibeufinCliDetails { + nexusUrl: string; + sandboxUrl: string; + nexusDatabaseUri: string; + sandboxDatabaseUri: string; + user: LibeufinNexusUser; +} + +export interface LibeufinEbicsSubscriberDetails { + hostId: string; + partnerId: string; + userId: string; +} + +export interface LibeufinEbicsConnectionDetails { + subscriberDetails: LibeufinEbicsSubscriberDetails; + ebicsUrl: string; + connectionName: string; +} + +export interface LibeufinBankAccountDetails { + currency: string; + iban: string; + bic: string; + personName: string; + accountName: string; +} + +export interface LibeufinNexusUser { + username: string; + password: string; +} + +export interface LibeufinBackupFileDetails { + passphrase: string; + outputFile: string; + connectionName: string; +} + +export interface LibeufinKeyLetterDetails { + outputFile: string; + connectionName: string; +} + +export interface LibeufinBankAccountImportDetails { + offeredBankAccountName: string; + nexusBankAccountName: string; + connectionName: string; +} + +export interface BankAccountInfo { + iban: string; + bic: string; + name: string; + currency: string; + label: string; +} + +export interface LibeufinPreparedPaymentDetails { + creditorIban: string; + creditorBic: string; + creditorName: string; + subject: string; + amount: string; + currency: string; + nexusBankAccountName: string; +} + +export interface LibeufinSandboxAddIncomingRequest { + creditorIban: string; + creditorBic: string; + creditorName: string; + debtorIban: string; + debtorBic: string; + debtorName: string; + subject: string; + amount: string; + currency: string; + uid: string; + direction: string; +} + +export class LibeufinSandboxService implements LibeufinSandboxServiceInterface { + static async create( + gc: GlobalTestState, + sandboxConfig: LibeufinSandboxConfig, + ): Promise<LibeufinSandboxService> { + return new LibeufinSandboxService(gc, sandboxConfig); + } + + sandboxProc: ProcessWrapper | undefined; + globalTestState: GlobalTestState; + + constructor( + gc: GlobalTestState, + private sandboxConfig: LibeufinSandboxConfig, + ) { + this.globalTestState = gc; + } + + get baseUrl(): string { + return `http://localhost:${this.sandboxConfig.httpPort}/`; + } + + async start(): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-sandbox-config", + "libeufin-sandbox config localhost", + { + ...process.env, + LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, + }, + ); + this.sandboxProc = this.globalTestState.spawnService( + "libeufin-sandbox", + ["serve", "--port", `${this.sandboxConfig.httpPort}`], + "libeufin-sandbox", + { + ...process.env, + LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, + LIBEUFIN_SANDBOX_ADMIN_PASSWORD: "secret", + }, + ); + } + + async c53tick(): Promise<string> { + const stdout = await sh( + this.globalTestState, + "libeufin-sandbox-c53tick", + "libeufin-sandbox camt053tick", + { + ...process.env, + LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, + }, + ); + return stdout; + } + + async makeTransaction( + debit: string, + credit: string, + amount: string, // $currency:x.y + subject: string,): Promise<string> { + const stdout = await sh( + this.globalTestState, + "libeufin-sandbox-maketransfer", + `libeufin-sandbox make-transaction --debit-account=${debit} --credit-account=${credit} ${amount} "${subject}"`, + { + ...process.env, + LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri, + }, + ); + return stdout; + } + + async pingUntilAvailable(): Promise<void> { + const url = this.baseUrl; + await pingProc(this.sandboxProc, url, "libeufin-sandbox"); + } +} + +export class LibeufinNexusService { + static async create( + gc: GlobalTestState, + nexusConfig: LibeufinNexusConfig, + ): Promise<LibeufinNexusService> { + return new LibeufinNexusService(gc, nexusConfig); + } + + nexusProc: ProcessWrapper | undefined; + globalTestState: GlobalTestState; + + constructor(gc: GlobalTestState, private nexusConfig: LibeufinNexusConfig) { + this.globalTestState = gc; + } + + get baseUrl(): string { + return `http://localhost:${this.nexusConfig.httpPort}/`; + } + + async start(): Promise<void> { + await runCommand( + this.globalTestState, + "libeufin-nexus-superuser", + "libeufin-nexus", + ["superuser", "admin", "--password", "test"], + { + ...process.env, + LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri, + }, + ); + + this.nexusProc = this.globalTestState.spawnService( + "libeufin-nexus", + ["serve", "--port", `${this.nexusConfig.httpPort}`], + "libeufin-nexus", + { + ...process.env, + LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri, + }, + ); + } + + async pingUntilAvailable(): Promise<void> { + const url = `${this.baseUrl}config`; + await pingProc(this.nexusProc, url, "libeufin-nexus"); + } + + async createNexusSuperuser(details: LibeufinNexusUser): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-nexus", + `libeufin-nexus superuser ${details.username} --password=${details.password}`, + { + ...process.env, + LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri, + }, + ); + console.log(stdout); + } +} + +export interface CreateEbicsSubscriberRequest { + hostID: string; + userID: string; + partnerID: string; + systemID?: string; +} + +export interface TwgAddIncomingRequest { + amount: string; + reserve_pub: string; + debit_account: string; +} + +interface CreateEbicsBankAccountRequest { + subscriber: { + hostID: string; + partnerID: string; + userID: string; + systemID?: string; + }; + // IBAN + iban: string; + // BIC + bic: string; + // human name + name: string; + currency: string; + label: string; +} + +export interface SimulateIncomingTransactionRequest { + debtorIban: string; + debtorBic: string; + debtorName: string; + + /** + * Subject / unstructured remittance info. + */ + subject: string; + + /** + * Decimal amount without currency. + */ + amount: string; +} + +/** + * The bundle aims at minimizing the amount of input + * data that is required to initialize a new user + Ebics + * connection. + */ +export class NexusUserBundle { + userReq: CreateNexusUserRequest; + connReq: CreateEbicsBankConnectionRequest; + anastasisReq: CreateAnastasisFacadeRequest; + twgReq: CreateTalerWireGatewayFacadeRequest; + twgTransferPermission: PostNexusPermissionRequest; + twgHistoryPermission: PostNexusPermissionRequest; + twgAddIncomingPermission: PostNexusPermissionRequest; + localAccountName: string; + remoteAccountName: string; + + constructor(salt: string, ebicsURL: string) { + this.userReq = { + username: `username-${salt}`, + password: `password-${salt}`, + }; + + this.connReq = { + name: `connection-${salt}`, + ebicsURL: ebicsURL, + hostID: `ebicshost,${salt}`, + partnerID: `ebicspartner,${salt}`, + userID: `ebicsuser,${salt}`, + }; + + this.twgReq = { + currency: "EUR", + name: `twg-${salt}`, + reserveTransferLevel: "report", + accountName: `local-account-${salt}`, + connectionName: `connection-${salt}`, + }; + this.anastasisReq = { + currency: "EUR", + name: `anastasis-${salt}`, + reserveTransferLevel: "report", + accountName: `local-account-${salt}`, + connectionName: `connection-${salt}`, + }; + this.remoteAccountName = `remote-account-${salt}`; + this.localAccountName = `local-account-${salt}`; + this.twgTransferPermission = { + action: "grant", + permission: { + subjectId: `username-${salt}`, + subjectType: "user", + resourceType: "facade", + resourceId: `twg-${salt}`, + permissionName: "facade.talerWireGateway.transfer", + }, + }; + this.twgHistoryPermission = { + action: "grant", + permission: { + subjectId: `username-${salt}`, + subjectType: "user", + resourceType: "facade", + resourceId: `twg-${salt}`, + permissionName: "facade.talerWireGateway.history", + }, + }; + } +} + +/** + * The bundle aims at minimizing the amount of input + * data that is required to initialize a new Sandbox + * customer, associating their bank account with a Ebics + * subscriber. + */ +export class SandboxUserBundle { + ebicsBankAccount: CreateEbicsBankAccountRequest; + constructor(salt: string) { + this.ebicsBankAccount = { + currency: "EUR", + bic: "BELADEBEXXX", + iban: getRandomIban("DE"), + label: `remote-account-${salt}`, + name: `Taler Exchange: ${salt}`, + subscriber: { + hostID: `ebicshost,${salt}`, + partnerID: `ebicspartner,${salt}`, + userID: `ebicsuser,${salt}`, + }, + }; + } +} + +export class LibeufinCli { + cliDetails: LibeufinCliDetails; + globalTestState: GlobalTestState; + + constructor(gc: GlobalTestState, cd: LibeufinCliDetails) { + this.globalTestState = gc; + this.cliDetails = cd; + } + + env(): any { + return { + ...process.env, + LIBEUFIN_SANDBOX_URL: this.cliDetails.sandboxUrl, + LIBEUFIN_SANDBOX_USERNAME: "admin", + LIBEUFIN_SANDBOX_PASSWORD: "secret", + } + } + + async checkSandbox(): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-checksandbox", + "libeufin-cli sandbox check", + this.env() + ); + } + + async createEbicsHost(hostId: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-createebicshost", + `libeufin-cli sandbox ebicshost create --host-id=${hostId}`, + this.env() + ); + console.log(stdout); + } + + async createEbicsSubscriber( + details: LibeufinEbicsSubscriberDetails, + ): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-createebicssubscriber", + "libeufin-cli sandbox ebicssubscriber create" + + ` --host-id=${details.hostId}` + + ` --partner-id=${details.partnerId}` + + ` --user-id=${details.userId}`, + this.env() + ); + console.log(stdout); + } + + async createEbicsBankAccount( + sd: LibeufinEbicsSubscriberDetails, + bankAccountDetails: LibeufinBankAccountDetails, + ): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-createebicsbankaccount", + "libeufin-cli sandbox ebicsbankaccount create" + + ` --currency=${bankAccountDetails.currency}` + + ` --iban=${bankAccountDetails.iban}` + + ` --bic=${bankAccountDetails.bic}` + + ` --person-name='${bankAccountDetails.personName}'` + + ` --account-name=${bankAccountDetails.accountName}` + + ` --ebics-host-id=${sd.hostId}` + + ` --ebics-partner-id=${sd.partnerId}` + + ` --ebics-user-id=${sd.userId}`, + this.env() + ); + console.log(stdout); + } + + async generateTransactions(accountName: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-generatetransactions", + `libeufin-cli sandbox bankaccount generate-transactions ${accountName}`, + this.env() + ); + console.log(stdout); + } + + async showSandboxTransactions(accountName: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-showsandboxtransactions", + `libeufin-cli sandbox bankaccount transactions ${accountName}`, + this.env() + ); + console.log(stdout); + } + + async createEbicsConnection( + connectionDetails: LibeufinEbicsConnectionDetails, + ): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-createebicsconnection", + `libeufin-cli connections new-ebics-connection` + + ` --ebics-url=${connectionDetails.ebicsUrl}` + + ` --host-id=${connectionDetails.subscriberDetails.hostId}` + + ` --partner-id=${connectionDetails.subscriberDetails.partnerId}` + + ` --ebics-user-id=${connectionDetails.subscriberDetails.userId}` + + ` ${connectionDetails.connectionName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async createBackupFile(details: LibeufinBackupFileDetails): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-createbackupfile", + `libeufin-cli connections export-backup` + + ` --passphrase=${details.passphrase}` + + ` --output-file=${details.outputFile}` + + ` ${details.connectionName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async createKeyLetter(details: LibeufinKeyLetterDetails): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-createkeyletter", + `libeufin-cli connections get-key-letter` + + ` ${details.connectionName} ${details.outputFile}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async connect(connectionName: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-connect", + `libeufin-cli connections connect ${connectionName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async downloadBankAccounts(connectionName: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-downloadbankaccounts", + `libeufin-cli connections download-bank-accounts ${connectionName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async listOfferedBankAccounts(connectionName: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-listofferedbankaccounts", + `libeufin-cli connections list-offered-bank-accounts ${connectionName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async importBankAccount( + importDetails: LibeufinBankAccountImportDetails, + ): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-importbankaccount", + "libeufin-cli connections import-bank-account" + + ` --offered-account-id=${importDetails.offeredBankAccountName}` + + ` --nexus-bank-account-id=${importDetails.nexusBankAccountName}` + + ` ${importDetails.connectionName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async fetchTransactions(bankAccountName: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-fetchtransactions", + `libeufin-cli accounts fetch-transactions ${bankAccountName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async transactions(bankAccountName: string): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-transactions", + `libeufin-cli accounts transactions ${bankAccountName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async preparePayment(details: LibeufinPreparedPaymentDetails): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-preparepayment", + `libeufin-cli accounts prepare-payment` + + ` --creditor-iban=${details.creditorIban}` + + ` --creditor-bic=${details.creditorBic}` + + ` --creditor-name='${details.creditorName}'` + + ` --payment-subject='${details.subject}'` + + ` --payment-amount=${details.currency}:${details.amount}` + + ` ${details.nexusBankAccountName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async submitPayment( + details: LibeufinPreparedPaymentDetails, + paymentUuid: string, + ): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-submitpayment", + `libeufin-cli accounts submit-payment` + + ` --payment-uuid=${paymentUuid}` + + ` ${details.nexusBankAccountName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async newAnastasisFacade(req: NewAnastasisFacadeReq): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-new-anastasis-facade", + `libeufin-cli facades new-anastasis-facade` + + ` --currency ${req.currency}` + + ` --facade-name ${req.facadeName}` + + ` ${req.connectionName} ${req.accountName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + + async newTalerWireGatewayFacade(req: NewTalerWireGatewayReq): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-new-taler-wire-gateway-facade", + `libeufin-cli facades new-taler-wire-gateway-facade` + + ` --currency ${req.currency}` + + ` --facade-name ${req.facadeName}` + + ` ${req.connectionName} ${req.accountName}`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } + + async listFacades(): Promise<void> { + const stdout = await sh( + this.globalTestState, + "libeufin-cli-facades-list", + `libeufin-cli facades list`, + { + ...process.env, + LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, + LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username, + LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password, + }, + ); + console.log(stdout); + } +} + +interface NewAnastasisFacadeReq { + facadeName: string; + connectionName: string; + accountName: string; + currency: string; +} + +interface NewTalerWireGatewayReq { + facadeName: string; + connectionName: string; + accountName: string; + currency: string; +} + +export namespace LibeufinSandboxApi { + + export async function rotateKeys( + libeufinSandboxService: LibeufinSandboxServiceInterface, + hostID: string, + ) { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL(`admin/ebics/hosts/${hostID}/rotate-keys`, baseUrl); + await axios.post(url.href, {}, { + auth: { + username: "admin", + password: "secret", + }, + }); + } + export async function createEbicsHost( + libeufinSandboxService: LibeufinSandboxServiceInterface, + hostID: string, + ) { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL("admin/ebics/hosts", baseUrl); + await axios.post(url.href, { + hostID, + ebicsVersion: "2.5", + }, + { + auth: { + username: "admin", + password: "secret", + }, + }); + } + + export async function createBankAccount( + libeufinSandboxService: LibeufinSandboxServiceInterface, + req: BankAccountInfo, + ) { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL(`admin/bank-accounts/${req.label}`, baseUrl); + await axios.post(url.href, req, { + auth: { + username: "admin", + password: "secret", + }, + }); + } + + export async function createEbicsSubscriber( + libeufinSandboxService: LibeufinSandboxServiceInterface, + req: CreateEbicsSubscriberRequest, + ) { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL("admin/ebics/subscribers", baseUrl); + await axios.post(url.href, req, { + auth: { + username: "admin", + password: "secret", + }, + }); + } + + export async function createEbicsBankAccount( + libeufinSandboxService: LibeufinSandboxServiceInterface, + req: CreateEbicsBankAccountRequest, + ) { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL("admin/ebics/bank-accounts", baseUrl); + await axios.post(url.href, req, { + auth: { + username: "admin", + password: "secret", + }, + }); + } + + export async function bookPayment2( + libeufinSandboxService: LibeufinSandboxService, + req: LibeufinSandboxAddIncomingRequest, + ) { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL("admin/payments", baseUrl); + await axios.post(url.href, req, { + auth: { + username: "admin", + password: "secret", + }, + }); + } + + export async function bookPayment( + libeufinSandboxService: LibeufinSandboxService, + creditorBundle: SandboxUserBundle, + debitorBundle: SandboxUserBundle, + subject: string, + amount: string, + currency: string, + ) { + let req: LibeufinSandboxAddIncomingRequest = { + creditorIban: creditorBundle.ebicsBankAccount.iban, + creditorBic: creditorBundle.ebicsBankAccount.bic, + creditorName: creditorBundle.ebicsBankAccount.name, + debtorIban: debitorBundle.ebicsBankAccount.iban, + debtorBic: debitorBundle.ebicsBankAccount.bic, + debtorName: debitorBundle.ebicsBankAccount.name, + subject: subject, + amount: amount, + currency: currency, + uid: getRandomString(), + direction: "CRDT", + }; + await bookPayment2(libeufinSandboxService, req); + } + + export async function simulateIncomingTransaction( + libeufinSandboxService: LibeufinSandboxServiceInterface, + accountLabel: string, + req: SimulateIncomingTransactionRequest, + ) { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL( + `admin/bank-accounts/${accountLabel}/simulate-incoming-transaction`, + baseUrl, + ); + await axios.post(url.href, req, { + auth: { + username: "admin", + password: "secret", + }, + }); + } + + export async function getAccountTransactions( + libeufinSandboxService: LibeufinSandboxServiceInterface, + accountLabel: string, + ): Promise<SandboxAccountTransactions> { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL( + `admin/bank-accounts/${accountLabel}/transactions`, + baseUrl, + ); + const res = await axios.get(url.href, { + auth: { + username: "admin", + password: "secret", + }, + }); + return res.data as SandboxAccountTransactions; + } + + export async function getCamt053( + libeufinSandboxService: LibeufinSandboxServiceInterface, + accountLabel: string, + ): Promise<any> { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL("admin/payments/camt", baseUrl); + return await axios.post(url.href, { + bankaccount: accountLabel, + type: 53, + }, + { + auth: { + username: "admin", + password: "secret", + }, + }); + } + + export async function getAccountInfoWithBalance( + libeufinSandboxService: LibeufinSandboxServiceInterface, + accountLabel: string, + ): Promise<any> { + const baseUrl = libeufinSandboxService.baseUrl; + let url = new URL( + `admin/bank-accounts/${accountLabel}`, + baseUrl, + ); + return await axios.get(url.href, { + auth: { + username: "admin", + password: "secret", + }, + }); + } +} + +export interface SandboxAccountTransactions { + payments: { + accountLabel: string; + creditorIban: string; + creditorBic?: string; + creditorName: string; + debtorIban: string; + debtorBic: string; + debtorName: string; + amount: string; + currency: string; + subject: string; + date: string; + creditDebitIndicator: "debit" | "credit"; + accountServicerReference: string; + }[]; +} + +export interface CreateEbicsBankConnectionRequest { + name: string; + ebicsURL: string; + hostID: string; + userID: string; + partnerID: string; + systemID?: string; +} + +export interface CreateAnastasisFacadeRequest { + name: string; + connectionName: string; + accountName: string; + currency: string; + reserveTransferLevel: "report" | "statement" | "notification"; +} + + +export interface CreateTalerWireGatewayFacadeRequest { + name: string; + connectionName: string; + accountName: string; + currency: string; + reserveTransferLevel: "report" | "statement" | "notification"; +} + +export interface UpdateNexusUserRequest { + newPassword: string; +} + +export interface NexusAuth { + auth: { + username: string; + password: string; + }; +} + +export interface CreateNexusUserRequest { + username: string; + password: string; +} + +export interface PostNexusTaskRequest { + name: string; + cronspec: string; + type: string; // fetch | submit + params: + | { + level: string; // report | statement | all + rangeType: string; // all | since-last | previous-days | latest + } + | {}; +} + +export interface PostNexusPermissionRequest { + action: "revoke" | "grant"; + permission: { + subjectType: string; + subjectId: string; + resourceType: string; + resourceId: string; + permissionName: string; + }; +} + +export namespace LibeufinNexusApi { + export async function getAllConnections( + nexus: LibeufinNexusServiceInterface, + ): Promise<any> { + let url = new URL("bank-connections", nexus.baseUrl); + const res = await axios.get(url.href, { + auth: { + username: "admin", + password: "test", + }, + }); + return res; + } + + export async function deleteBankConnection( + libeufinNexusService: LibeufinNexusServiceInterface, + req: DeleteBankConnectionRequest, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL("bank-connections/delete-connection", baseUrl); + return await axios.post(url.href, req, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function createEbicsBankConnection( + libeufinNexusService: LibeufinNexusServiceInterface, + req: CreateEbicsBankConnectionRequest, + ): Promise<void> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL("bank-connections", baseUrl); + await axios.post( + url.href, + { + source: "new", + type: "ebics", + name: req.name, + data: { + ebicsURL: req.ebicsURL, + hostID: req.hostID, + userID: req.userID, + partnerID: req.partnerID, + systemID: req.systemID, + }, + }, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + export async function getBankAccount( + libeufinNexusService: LibeufinNexusServiceInterface, + accountName: string, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `bank-accounts/${accountName}`, + baseUrl, + ); + return await axios.get( + url.href, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + + export async function submitInitiatedPayment( + libeufinNexusService: LibeufinNexusServiceInterface, + accountName: string, + paymentId: string, + ): Promise<void> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `bank-accounts/${accountName}/payment-initiations/${paymentId}/submit`, + baseUrl, + ); + await axios.post( + url.href, + {}, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + export async function fetchAccounts( + libeufinNexusService: LibeufinNexusServiceInterface, + connectionName: string, + ): Promise<void> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `bank-connections/${connectionName}/fetch-accounts`, + baseUrl, + ); + await axios.post( + url.href, + {}, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + export async function importConnectionAccount( + libeufinNexusService: LibeufinNexusServiceInterface, + connectionName: string, + offeredAccountId: string, + nexusBankAccountId: string, + ): Promise<void> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `bank-connections/${connectionName}/import-account`, + baseUrl, + ); + await axios.post( + url.href, + { + offeredAccountId, + nexusBankAccountId, + }, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + export async function connectBankConnection( + libeufinNexusService: LibeufinNexusServiceInterface, + connectionName: string, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`bank-connections/${connectionName}/connect`, baseUrl); + await axios.post( + url.href, + {}, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + export async function getPaymentInitiations( + libeufinNexusService: LibeufinNexusService, + accountName: string, + username: string = "admin", + password: string = "test", + ): Promise<void> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `/bank-accounts/${accountName}/payment-initiations`, + baseUrl, + ); + let response = await axios.get(url.href, { + auth: { + username: username, + password: password, + }, + }); + console.log( + `Payment initiations of: ${accountName}`, + JSON.stringify(response.data, null, 2), + ); + } + + export async function getConfig( + libeufinNexusService: LibeufinNexusService, + ): Promise<void> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/config`, baseUrl); + let response = await axios.get(url.href); + } + + // Uses the Anastasis API to get a list of transactions. + export async function getAnastasisTransactions( + libeufinNexusService: LibeufinNexusService, + anastasisBaseUrl: string, + params: {}, // of the request: {delta: 5, ..} + username: string = "admin", + password: string = "test", + ): Promise<any> { + let url = new URL("history/incoming", anastasisBaseUrl); + let response = await axios.get(url.href, { params: params, + auth: { + username: username, + password: password, + }, + }); + return response; + } + + // FIXME: this function should return some structured + // object that represents a history. + export async function getAccountTransactions( + libeufinNexusService: LibeufinNexusService, + accountName: string, + username: string = "admin", + password: string = "test", + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/bank-accounts/${accountName}/transactions`, baseUrl); + let response = await axios.get(url.href, { + auth: { + username: username, + password: password, + }, + }); + return response; + } + + export async function fetchTransactions( + libeufinNexusService: LibeufinNexusService, + accountName: string, + rangeType: string = "all", + level: string = "report", + username: string = "admin", + password: string = "test", + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `/bank-accounts/${accountName}/fetch-transactions`, + baseUrl, + ); + return await axios.post( + url.href, + { + rangeType: rangeType, + level: level, + }, + { + auth: { + username: username, + password: password, + }, + }, + ); + } + + export async function changePassword( + libeufinNexusService: LibeufinNexusServiceInterface, + username: string, + req: UpdateNexusUserRequest, + auth: NexusAuth, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/users/${username}/password`, baseUrl); + await axios.post(url.href, req, auth); + } + + export async function getUser( + libeufinNexusService: LibeufinNexusServiceInterface, + auth: NexusAuth, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/user`, baseUrl); + return await axios.get(url.href, auth); + } + + export async function createUser( + libeufinNexusService: LibeufinNexusServiceInterface, + req: CreateNexusUserRequest, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/users`, baseUrl); + await axios.post(url.href, req, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function getAllPermissions( + libeufinNexusService: LibeufinNexusServiceInterface, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/permissions`, baseUrl); + return await axios.get(url.href, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function postPermission( + libeufinNexusService: LibeufinNexusServiceInterface, + req: PostNexusPermissionRequest, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/permissions`, baseUrl); + await axios.post(url.href, req, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function getTasks( + libeufinNexusService: LibeufinNexusServiceInterface, + bankAccountName: string, + // When void, the request returns the list of all the + // tasks under this bank account. + taskName: string | void, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); + if (taskName) url = new URL(taskName, `${url}/`); + + // It's caller's responsibility to interpret the response. + return await axios.get(url.href, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function deleteTask( + libeufinNexusService: LibeufinNexusServiceInterface, + bankAccountName: string, + taskName: string, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `/bank-accounts/${bankAccountName}/schedule/${taskName}`, + baseUrl, + ); + await axios.delete(url.href, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function postTask( + libeufinNexusService: LibeufinNexusServiceInterface, + bankAccountName: string, + req: PostNexusTaskRequest, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); + return await axios.post(url.href, req, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function deleteFacade( + libeufinNexusService: LibeufinNexusServiceInterface, + facadeName: string, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL(`facades/${facadeName}`, baseUrl); + return await axios.delete(url.href, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function getAllFacades( + libeufinNexusService: LibeufinNexusServiceInterface, + ): Promise<any> { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL("facades", baseUrl); + return await axios.get(url.href, { + auth: { + username: "admin", + password: "test", + }, + }); + } + + export async function createAnastasisFacade( + libeufinNexusService: LibeufinNexusServiceInterface, + req: CreateAnastasisFacadeRequest, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL("facades", baseUrl); + await axios.post( + url.href, + { + name: req.name, + type: "anastasis", + config: { + bankAccount: req.accountName, + bankConnection: req.connectionName, + currency: req.currency, + reserveTransferLevel: req.reserveTransferLevel, + }, + }, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + export async function createTwgFacade( + libeufinNexusService: LibeufinNexusServiceInterface, + req: CreateTalerWireGatewayFacadeRequest, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL("facades", baseUrl); + await axios.post( + url.href, + { + name: req.name, + type: "taler-wire-gateway", + config: { + bankAccount: req.accountName, + bankConnection: req.connectionName, + currency: req.currency, + reserveTransferLevel: req.reserveTransferLevel, + }, + }, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } + + export async function submitAllPaymentInitiations( + libeufinNexusService: LibeufinNexusServiceInterface, + accountId: string, + ) { + const baseUrl = libeufinNexusService.baseUrl; + let url = new URL( + `/bank-accounts/${accountId}/submit-all-payment-initiations`, + baseUrl, + ); + await axios.post( + url.href, + {}, + { + auth: { + username: "admin", + password: "test", + }, + }, + ); + } +} + +/** + * Launch Nexus and Sandbox AND creates users / facades / bank accounts / + * .. all that's required to start making banking traffic. + */ +export async function launchLibeufinServices( + t: GlobalTestState, + nexusUserBundle: NexusUserBundle[], + sandboxUserBundle: SandboxUserBundle[] = [], + withFacades: string[] = [], // takes only "twg" and/or "anastasis" +): Promise<LibeufinServices> { + const db = await setupDb(t); + + const libeufinSandbox = await LibeufinSandboxService.create(t, { + httpPort: 5010, + databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`, + }); + + await libeufinSandbox.start(); + await libeufinSandbox.pingUntilAvailable(); + + const libeufinNexus = await LibeufinNexusService.create(t, { + httpPort: 5011, + databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`, + }); + + await libeufinNexus.start(); + await libeufinNexus.pingUntilAvailable(); + console.log("Libeufin services launched!"); + + for (let sb of sandboxUserBundle) { + await LibeufinSandboxApi.createEbicsHost( + libeufinSandbox, + sb.ebicsBankAccount.subscriber.hostID, + ); + await LibeufinSandboxApi.createEbicsSubscriber( + libeufinSandbox, + sb.ebicsBankAccount.subscriber, + ); + await LibeufinSandboxApi.createEbicsBankAccount( + libeufinSandbox, + sb.ebicsBankAccount, + ); + } + console.log("Sandbox user(s) / account(s) / subscriber(s): created"); + + for (let nb of nexusUserBundle) { + await LibeufinNexusApi.createEbicsBankConnection(libeufinNexus, nb.connReq); + await LibeufinNexusApi.connectBankConnection( + libeufinNexus, + nb.connReq.name, + ); + await LibeufinNexusApi.fetchAccounts(libeufinNexus, nb.connReq.name); + await LibeufinNexusApi.importConnectionAccount( + libeufinNexus, + nb.connReq.name, + nb.remoteAccountName, + nb.localAccountName, + ); + await LibeufinNexusApi.createUser(libeufinNexus, nb.userReq); + for (let facade of withFacades) { + switch (facade) { + case "twg": + await LibeufinNexusApi.createTwgFacade(libeufinNexus, nb.twgReq); + await LibeufinNexusApi.postPermission( + libeufinNexus, + nb.twgTransferPermission, + ); + await LibeufinNexusApi.postPermission( + libeufinNexus, + nb.twgHistoryPermission, + ); + break; + case "anastasis": + await LibeufinNexusApi.createAnastasisFacade(libeufinNexus, nb.anastasisReq); + } + } + } + console.log( + "Nexus user(s) / connection(s) / facade(s) / permission(s): created", + ); + + return { + commonDb: db, + libeufinNexus: libeufinNexus, + libeufinSandbox: libeufinSandbox, + }; +} + +/** + * Helper function that searches a payment among + * a list, as returned by Nexus. The key is just + * the payment subject. + */ +export function findNexusPayment( + key: string, + payments: LibeufinNexusTransactions, +): LibeufinNexusMoneyMovement | void { + let transactions = payments["transactions"]; + for (let i = 0; i < transactions.length; i++) { + let batches = transactions[i]["batches"]; + for (let y = 0; y < batches.length; y++) { + let movements = batches[y]["batchTransactions"]; + for (let z = 0; z < movements.length; z++) { + let movement = movements[z]; + if (movement["details"]["unstructuredRemittanceInformation"] == key) + return movement; + } + } + } +} |