/* 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 */ /** * This file defines euFin test logic that needs state * and that depends on the main harness.ts. The other * definitions - mainly helper functions to call RESTful * APIs - moved to libeufin-apis.ts. That enables harness.ts * to depend on such API calls, in contrast to the previous * situation where harness.ts had to include this file causing * a circular dependency. */ /** * Imports. */ import { AmountString, Logger } from "@gnu-taler/taler-util"; import { DbInfo, GlobalTestState, ProcessWrapper, getRandomIban, pingProc, runCommand, setupDb, sh, } from "../harness/harness.js"; import { CreateAnastasisFacadeRequest, CreateEbicsBankAccountRequest, CreateEbicsBankConnectionRequest, CreateNexusUserRequest, CreateTalerWireGatewayFacadeRequest, LibeufinNexusApi, LibeufinSandboxApi, LibeufinSandboxServiceInterface, PostNexusPermissionRequest, } from "../harness/libeufin-apis.js"; const logger = new Logger("libeufin.ts"); export { LibeufinNexusApi, LibeufinSandboxApi }; 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 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; }; } export interface LibeufinNexusBatches { batchTransactions: Array; } export interface LibeufinNexusTransaction { amount: string; creditDebitIndicator: string; status: string; bankTransactionCode: string; valueDate: string; bookingDate: string; accountServicerRef: string; batches: Array; } export interface LibeufinNexusTransactions { transactions: Array; } export interface LibeufinCliDetails { nexusUrl: string; sandboxUrl: string; nexusDatabaseUri: string; sandboxDatabaseUri: string; nexusUser: 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 LibeufinPreparedPaymentDetails { creditorIban: string; creditorBic: string; creditorName: string; subject: string; amount: string; currency: string; nexusBankAccountName: string; } export interface NexusBankConnection { // connection type. For example "ebics". type: string; // connection name as given by the user at // the moment of creation. name: string; } export interface NexusBankConnections { bankConnections: NexusBankConnection[]; } export interface FacadeShowInfo { // Name of the facade, same as the "fcid" parameter. name: string; // Type of the facade. // For example, "taler-wire-gateway". type: string; // Bas URL of the facade. baseUrl: string; // details depending on the facade type. config: any; } export interface FetchParams { // Because transactions are delivered by banks in "batches", // then every batch can have different qualities. This value // lets the request specify which type of batch ought to be // returned. Currently, the following two type are supported: // // 'report': typically includes only non booked transactions. // 'statement': typically includes only booked transactions. level: "report" | "statement" | "all"; // This type indicates the time range of the query. // It allows the following values: // // 'latest': retrieves the last transactions from the bank. // If there are older unread transactions, those will *not* // be downloaded. // // 'all': retrieves all the transactions from the bank, // until the oldest. // // 'previous-days': currently *not* implemented, it will allow // the request to download transactions from // today until N days before. // // 'since-last': retrieves all the transactions since the last // time one was downloaded. // rangeType: "latest" | "all" | "previous-days" | "since-last"; } export interface NexusTask { // The resource being impacted by this operation. // Typically a (Nexus) bank account being fetched // or whose payments are submitted. In this cases, // this value is the "bank-account" constant. resourceType: string; // Name of the resource. In case of "bank-account", that // is the name under which the bank account was imported // from the bank. resourceId: string; // Task name, equals 'taskId' taskName: string; // Values allowed are "fetch" or "submit". taskType: string; // FIXME: describe. taskCronSpec: string; // Only meaningful for "fetch" types. taskParams: FetchParams; // Timestamp in secons when the next iteration will run. nextScheduledExecutionSec: number; // Timestamp in seconds when the previous iteration ran. prevScheduledExecutionSec: number; } export interface NexusNewTransactionsInfo { // How many transactions are new to Nexus. newTransactions: number; // How many transactions got downloaded by the request. // Note that a transaction can be downloaded multiple // times but only counts as new once. downloadedTransactions: number; } export interface NexusUserResponse { // User name username: string; // Is this a super user? superuser: boolean; } export interface NexusTaskShortInfo { cronspec: string; type: "fetch" | "submit"; params: FetchParams; } export interface NexusTaskCollection { // This field can contain *multiple* objects of the type sampled below. schedule: { [taskName: string]: NexusTaskShortInfo; }; } export interface NexusFacadeListResponse { facades: FacadeShowInfo[]; } export interface LibeufinSandboxAdminBankAccountBalance { // Balance in the $currency:$amount format. balance: AmountString; // IBAN of the bank account identified by $accountLabel iban: string; // BIC of the bank account identified by $accountLabel bic: string; // Mentions $accountLabel label: string; } export interface LibeufinPermission { subjectType: string; subjectId: string; resourceType: string; resourceId: string; permissionName: string; } export interface NexusGetPermissionsResponse { permissions: LibeufinPermission[]; } export class LibeufinSandboxService implements LibeufinSandboxServiceInterface { static async create( gc: GlobalTestState, sandboxConfig: LibeufinSandboxConfig, ): Promise { 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 { await sh( this.globalTestState, "libeufin-sandbox-config", "libeufin-sandbox config default", { ...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 { 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 { 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 { const url = this.baseUrl; await pingProc(this.sandboxProc, url, "libeufin-sandbox"); } } export class LibeufinNexusService { static async create( gc: GlobalTestState, nexusConfig: LibeufinNexusConfig, ): Promise { 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 { 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 { const url = `${this.baseUrl}config`; await pingProc(this.nexusProc, url, "libeufin-nexus"); } async createNexusSuperuser(details: LibeufinNexusUser): Promise { 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 TwgAddIncomingRequest { amount: string; reserve_pub: string; debit_account: 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 = { bic: "BELADEBEXXX", iban: getRandomIban(), 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 { const stdout = await sh( this.globalTestState, "libeufin-cli-checksandbox", "libeufin-cli sandbox check", this.env(), ); } async registerBankCustomer( username: string, password: string, ): Promise { const stdout = await sh( this.globalTestState, "libeufin-cli-registercustomer", "libeufin-cli sandbox demobank register --name='Test Customer'", { ...process.env, LIBEUFIN_SANDBOX_URL: this.cliDetails.sandboxUrl + "/demobanks/default", LIBEUFIN_SANDBOX_USERNAME: username, LIBEUFIN_SANDBOX_PASSWORD: password, }, ); console.log(stdout); } async createEbicsHost(hostId: string): Promise { 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 { 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 { const stdout = await sh( this.globalTestState, "libeufin-cli-createebicsbankaccount", "libeufin-cli sandbox ebicsbankaccount create" + ` --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 { 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 { 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 { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async createBackupFile(details: LibeufinBackupFileDetails): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async createKeyLetter(details: LibeufinKeyLetterDetails): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async connect(connectionName: string): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async downloadBankAccounts(connectionName: string): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async listOfferedBankAccounts(connectionName: string): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async importBankAccount( importDetails: LibeufinBankAccountImportDetails, ): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async fetchTransactions(bankAccountName: string): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async transactions(bankAccountName: string): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async preparePayment(details: LibeufinPreparedPaymentDetails): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async submitPayment( details: LibeufinPreparedPaymentDetails, paymentUuid: string, ): Promise { const stdout = await sh( this.globalTestState, "libeufin-cli-submitpayments", `libeufin-cli accounts submit-payments` + ` --payment-uuid=${paymentUuid}` + ` ${details.nexusBankAccountName}`, { ...process.env, LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl, LIBEUFIN_NEXUS_USERNAME: this.cliDetails.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async newAnastasisFacade(req: NewAnastasisFacadeReq): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async newTalerWireGatewayFacade(req: NewTalerWireGatewayReq): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } async listFacades(): Promise { 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.nexusUser.username, LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.nexusUser.password, }, ); console.log(stdout); } } interface NewAnastasisFacadeReq { facadeName: string; connectionName: string; accountName: string; currency: string; } interface NewTalerWireGatewayReq { facadeName: string; connectionName: string; accountName: string; currency: string; } /** * Launch Nexus and Sandbox AND creates users / facades / bank accounts / * .. all that's required to start making bank traffic. */ export async function launchLibeufinServices( t: GlobalTestState, nexusUserBundle: NexusUserBundle[], sandboxUserBundle: SandboxUserBundle[] = [], withFacades: string[] = [], // takes only "twg" and/or "anastasis" ): Promise { 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.createDemobankAccount( sb.ebicsBankAccount.label, "password-unused", { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/access-api/" }, ); await LibeufinSandboxApi.createDemobankEbicsSubscriber( sb.ebicsBankAccount.subscriber, sb.ebicsBankAccount.label, { baseUrl: libeufinSandbox.baseUrl + "/demobanks/default/" }, ); } 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++) { //FIXME: last line won't compile with the current definition of the type //@ts-ignore let batches = transactions[i]["camtData"]["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; } } } }