Implementing euFin-based harness.

- move stateless euFin API helpers to separate
  file, in order to avoid circular dependecy between
  harness.ts and libeufin.ts

- implement BankServiceInterface with euFin.  This
  one is not tested and disabled by default.
This commit is contained in:
ms 2021-11-04 13:37:32 +01:00
parent 1b9cbc079a
commit c87be3707e
No known key found for this signature in database
GPG Key ID: 8D526861953F4C0F
4 changed files with 1088 additions and 840 deletions

View File

@ -67,6 +67,7 @@ import {
getRandomBytes, getRandomBytes,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { CoinConfig } from "./denomStructures.js"; import { CoinConfig } from "./denomStructures.js";
import { LibeufinNexusApi, LibeufinSandboxApi } from "./libeufin-apis.js";
const exec = util.promisify(require("child_process").exec); const exec = util.promisify(require("child_process").exec);
@ -607,7 +608,200 @@ export namespace BankApi {
} }
} }
export class BankService implements BankServiceInterface {
class BankServiceBase {
proc: ProcessWrapper | undefined;
protected constructor(
protected globalTestState: GlobalTestState,
protected bankConfig: BankConfig,
protected configFile: string,
) {}
}
/**
* Work in progress. The key point is that both Sandbox and Nexus
* will be configured and started by this class.
*/
class LibeufinBankService extends BankServiceBase implements BankService {
sandboxProc: ProcessWrapper | undefined;
nexusProc: ProcessWrapper | undefined;
static async create(
gc: GlobalTestState,
bc: BankConfig,
): Promise<BankService> {
return new LibeufinBankService(gc, bc, "foo");
}
get port() {
return this.bankConfig.httpPort;
}
get nexusBaseUrl(): string {
return `http://localhost:${this.bankConfig.httpPort + 1}`;
}
get baseUrl(): string {
return `http://localhost:${this.bankConfig.httpPort}/demobanks/default/access-api`;
}
async setSuggestedExchange(
e: ExchangeServiceInterface,
exchangePayto: string
) {
await sh(
this.globalTestState,
"libeufin-sandbox-set-default-exchange",
`libeufin-sandbox default-exchange ${exchangePayto}`
);
}
// Create one at both sides: Sandbox and Nexus.
async createExchangeAccount(
accountName: string,
password: string,
): Promise<HarnessExchangeBankAccount> {
await LibeufinSandboxApi.createDemobankAccount(
accountName,
password,
{ baseUrl: this.baseUrl }
);
let bankAccountLabel = `${accountName}-acct`
await LibeufinSandboxApi.createDemobankEbicsSubscriber(
{
hostID: "talertest-ebics-host",
userID: "exchange-ebics-user",
partnerID: "exchange-ebics-partner",
},
bankAccountLabel,
{ baseUrl: this.baseUrl }
);
await LibeufinNexusApi.createUser(
{ baseUrl: this.nexusBaseUrl },
{
username: `${accountName}-nexus-username`,
password: `${password}-nexus-password`
}
);
await LibeufinNexusApi.createEbicsBankConnection(
{ baseUrl: this.nexusBaseUrl },
{
name: "ebics-connection", // connection name.
ebicsURL: `http://localhost:${this.bankConfig.httpPort}/ebicsweb`,
hostID: "talertest-ebics-host",
userID: "exchange-ebics-user",
partnerID: "exchange-ebics-partner",
}
);
await LibeufinNexusApi.connectBankConnection(
{ baseUrl: this.nexusBaseUrl }, "ebics-connection"
);
await LibeufinNexusApi.fetchAccounts(
{ baseUrl: this.nexusBaseUrl }, "ebics-connection"
);
await LibeufinNexusApi.importConnectionAccount(
{ baseUrl: this.nexusBaseUrl },
"ebics-connection", // connection name
`${accountName}-acct`, // offered account label
`${accountName}-nexus-label` // bank account label at Nexus
);
await LibeufinNexusApi.createTwgFacade(
{ baseUrl: this.nexusBaseUrl },
{
name: "exchange-facade",
connectionName: "ebics-connection",
accountName: `${accountName}-nexus-label`,
currency: "EUR",
reserveTransferLevel: "report"
}
);
await LibeufinNexusApi.postPermission(
{ baseUrl: this.nexusBaseUrl },
{
action: "grant",
permission: {
subjectId: `${accountName}-nexus-username`,
subjectType: "user",
resourceType: "facade",
resourceId: "exchange-facade", // facade name
permissionName: "facade.talerWireGateway.transfer",
},
}
);
await LibeufinNexusApi.postPermission(
{ baseUrl: this.nexusBaseUrl },
{
action: "grant",
permission: {
subjectId: `${accountName}-nexus-username`,
subjectType: "user",
resourceType: "facade",
resourceId: "exchange-facade", // facade name
permissionName: "facade.talerWireGateway.history",
},
}
);
let facadesResp = await LibeufinNexusApi.getAllFacades({ baseUrl: this.nexusBaseUrl });
let accountInfoResp = await LibeufinSandboxApi.demobankAccountInfo(
accountName, // username
password,
{ baseUrl: this.nexusBaseUrl },
`${accountName}acct` // bank account label.
);
return {
accountName: accountName,
accountPassword: password,
accountPaytoUri: accountInfoResp.data.paytoUri,
wireGatewayApiBaseUrl: facadesResp.data.facades[0].baseUrl,
};
}
async start(): Promise<void> {
let sandboxDb = `jdbc:sqlite:${this.globalTestState.testDir}/libeufin-sandbox.sqlite3`;
let nexusDb = `jdbc:sqlite:${this.globalTestState.testDir}/libeufin-nexus.sqlite3`;
this.sandboxProc = this.globalTestState.spawnService(
"libeufin-sandbox",
["serve", "--port", `${this.port}`],
"libeufin-sandbox",
{
...process.env,
LIBEUFIN_SANDBOX_DB_CONNECTION: sandboxDb,
LIBEUFIN_SANDBOX_ADMIN_PASSWORD: "secret",
},
);
await runCommand(
this.globalTestState,
"libeufin-nexus-superuser",
"libeufin-nexus",
["superuser", "admin", "--password", "test"],
{
...process.env,
LIBEUFIN_NEXUS_DB_CONNECTION: nexusDb,
},
);
this.nexusProc = this.globalTestState.spawnService(
"libeufin-nexus",
["serve", "--port", `${this.port + 1}`],
"libeufin-nexus",
{
...process.env,
LIBEUFIN_NEXUS_DB_CONNECTION: nexusDb,
},
);
}
async pingUntilAvailable(): Promise<void> {
await pingProc(this.sandboxProc, this.baseUrl, "libeufin-sandbox");
await pingProc(this.nexusProc, `${this.baseUrl}config`, "libeufin-nexus");
}
}
export class BankService extends BankServiceBase implements BankServiceInterface {
proc: ProcessWrapper | undefined; proc: ProcessWrapper | undefined;
static async create( static async create(
@ -685,12 +879,6 @@ export class BankService implements BankServiceInterface {
return this.bankConfig.httpPort; return this.bankConfig.httpPort;
} }
private constructor(
private globalTestState: GlobalTestState,
private bankConfig: BankConfig,
private configFile: string,
) {}
async start(): Promise<void> { async start(): Promise<void> {
this.proc = this.globalTestState.spawnService( this.proc = this.globalTestState.spawnService(
"taler-bank-manage", "taler-bank-manage",
@ -705,16 +893,11 @@ export class BankService implements BankServiceInterface {
} }
} }
/** // Still work in progress..
* euFin migration: if (false && process.env.WALLET_HARNESS_WITH_EUFIN) {
* BankService.create = LibeufinBankService.create;
* if (process.env.WALLET_HARNESS_WITH_EUFIN) { BankService.prototype = Object.create(LibeufinBankService.prototype);
* BankService.prototype = { }
* // methods from euFin ..
* };
* }
*
*/
export class FakeBankService { export class FakeBankService {
proc: ProcessWrapper | undefined; proc: ProcessWrapper | undefined;

View File

@ -62,14 +62,6 @@ export interface SimpleTestEnvironment {
wallet: WalletCli; wallet: WalletCli;
} }
export function getRandomIban(countryCode: string): string {
return `${countryCode}715001051796${(Math.random().toString().substring(2, 8))}`
}
export function getRandomString(): string {
return Math.random().toString(36).substring(2);
}
/** /**
* Run a test case with a simple TESTKUDOS Taler environment, consisting * Run a test case with a simple TESTKUDOS Taler environment, consisting
* of one exchange, one bank and one merchant. * of one exchange, one bank and one merchant.

View File

@ -0,0 +1,857 @@
/**
* This file defines most of the API calls offered
* by Nexus and Sandbox. They don't have state,
* therefore got moved away from libeufin.ts where
* the services get actually started and managed.
*/
import axios from "axios";
import { URL } from "@gnu-taler/taler-util";
export interface LibeufinSandboxServiceInterface {
baseUrl: string;
}
export interface LibeufinNexusServiceInterface {
baseUrl: string;
}
export interface CreateEbicsSubscriberRequest {
hostID: string;
userID: string;
partnerID: string;
systemID?: string;
}
export interface BankAccountInfo {
iban: string;
bic: string;
name: string;
label: string;
}
export interface CreateEbicsBankConnectionRequest {
name: string; // connection name.
ebicsURL: string;
hostID: string;
userID: string;
partnerID: string;
systemID?: string;
}
export interface UpdateNexusUserRequest {
newPassword: string;
}
export interface NexusAuth {
auth: {
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 CreateNexusUserRequest {
username: string;
password: string;
}
export interface PostNexusPermissionRequest {
action: "revoke" | "grant";
permission: {
subjectType: string;
subjectId: string;
resourceType: string;
resourceId: string;
permissionName: 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 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 DeleteBankConnectionRequest {
bankConnectionId: string;
}
export interface SimulateIncomingTransactionRequest {
debtorIban: string;
debtorBic: string;
debtorName: string;
/**
* Subject / unstructured remittance info.
*/
subject: string;
/**
* Decimal amount without currency.
*/
amount: string;
}
export interface CreateEbicsBankAccountRequest {
subscriber: {
hostID: string;
partnerID: string;
userID: string;
systemID?: string;
};
// IBAN
iban: string;
// BIC
bic: string;
// human name
name: string;
label: 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;
}
function getRandomString(): string {
return Math.random().toString(36).substring(2);
}
export namespace LibeufinSandboxApi {
/**
* Return balance and payto-address of 'accountLabel'.
* Note: the demobank serving the request is hard-coded
* inside the base URL, and therefore contained in
* 'libeufinSandboxService'.
*/
export async function demobankAccountInfo(
username: string,
password: string,
libeufinSandboxService: LibeufinSandboxServiceInterface,
accountLabel: string
) {
let url = new URL(`${libeufinSandboxService.baseUrl}/accounts/${accountLabel}`);
return await axios.get(url.href, {
auth: {
username: username,
password: password
}
});
}
// Creates one bank account via the Access API.
export async function createDemobankAccount(
username: string,
password: string,
libeufinSandboxService: LibeufinSandboxServiceInterface,
) {
let url = new URL(`${libeufinSandboxService.baseUrl}/testing/register`);
await axios.post(url.href, {
username: username,
password: password
});
}
export async function createDemobankEbicsSubscriber(
req: CreateEbicsSubscriberRequest,
demobankAccountLabel: string,
libeufinSandboxService: LibeufinSandboxServiceInterface,
username: string = "admin",
password: string = "secret",
) {
// baseUrl should already be pointed to one demobank.
let url = new URL(libeufinSandboxService.baseUrl);
await axios.post(url.href, {
userID: req.userID,
hostID: req.hostID,
partnerID: req.userID,
demobankAccountLabel: demobankAccountLabel,
}, {
auth: {
username: "admin",
password: "secret",
},
});
}
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",
},
});
}
/**
* This function is useless. It creates a Ebics subscriber
* but never gives it a bank account. To be removed
*/
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 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 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: LibeufinNexusServiceInterface,
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: LibeufinNexusServiceInterface,
): 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: LibeufinNexusServiceInterface,
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: LibeufinNexusServiceInterface,
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: LibeufinNexusServiceInterface,
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",
},
},
);
}
}

View File

@ -14,12 +14,20 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/**
* 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. * Imports.
*/ */
import axios from "axios"; import axios from "axios";
import { URL } from "@gnu-taler/taler-util"; import { URL } from "@gnu-taler/taler-util";
import { getRandomIban, getRandomString } from "../harness/helpers.js";
import { import {
GlobalTestState, GlobalTestState,
DbInfo, DbInfo,
@ -30,12 +38,27 @@ import {
sh, sh,
} from "../harness/harness.js"; } from "../harness/harness.js";
export interface LibeufinSandboxServiceInterface { import {
baseUrl: string; LibeufinSandboxApi,
} LibeufinNexusApi,
CreateEbicsBankAccountRequest,
LibeufinSandboxServiceInterface,
CreateTalerWireGatewayFacadeRequest,
SimulateIncomingTransactionRequest,
SandboxAccountTransactions,
DeleteBankConnectionRequest,
CreateEbicsBankConnectionRequest,
UpdateNexusUserRequest,
NexusAuth,
CreateAnastasisFacadeRequest,
PostNexusTaskRequest,
PostNexusPermissionRequest,
CreateNexusUserRequest
} from "../harness/libeufin-apis.js";
export interface LibeufinNexusServiceInterface { export {
baseUrl: string; LibeufinSandboxApi,
LibeufinNexusApi
} }
export interface LibeufinServices { export interface LibeufinServices {
@ -54,10 +77,6 @@ export interface LibeufinNexusConfig {
databaseJdbcUri: string; databaseJdbcUri: string;
} }
export interface DeleteBankConnectionRequest {
bankConnectionId: string;
}
interface LibeufinNexusMoneyMovement { interface LibeufinNexusMoneyMovement {
amount: string; amount: string;
creditDebitIndicator: string; creditDebitIndicator: string;
@ -154,13 +173,6 @@ export interface LibeufinBankAccountImportDetails {
connectionName: string; connectionName: string;
} }
export interface BankAccountInfo {
iban: string;
bic: string;
name: string;
label: string;
}
export interface LibeufinPreparedPaymentDetails { export interface LibeufinPreparedPaymentDetails {
creditorIban: string; creditorIban: string;
creditorBic: string; creditorBic: string;
@ -171,18 +183,8 @@ export interface LibeufinPreparedPaymentDetails {
nexusBankAccountName: string; nexusBankAccountName: string;
} }
export interface LibeufinSandboxAddIncomingRequest { function getRandomIban(countryCode: string): string {
creditorIban: string; return `${countryCode}715001051796${(Math.random().toString().substring(2, 8))}`
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 { export class LibeufinSandboxService implements LibeufinSandboxServiceInterface {
@ -317,51 +319,12 @@ export class LibeufinNexusService {
} }
} }
export interface CreateEbicsSubscriberRequest {
hostID: string;
userID: string;
partnerID: string;
systemID?: string;
}
export interface TwgAddIncomingRequest { export interface TwgAddIncomingRequest {
amount: string; amount: string;
reserve_pub: string; reserve_pub: string;
debit_account: 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;
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 * The bundle aims at minimizing the amount of input
* data that is required to initialize a new user + Ebics * data that is required to initialize a new user + Ebics
@ -756,7 +719,6 @@ export class LibeufinCli {
console.log(stdout); console.log(stdout);
} }
async newTalerWireGatewayFacade(req: NewTalerWireGatewayReq): Promise<void> { async newTalerWireGatewayFacade(req: NewTalerWireGatewayReq): Promise<void> {
const stdout = await sh( const stdout = await sh(
this.globalTestState, this.globalTestState,
@ -805,752 +767,6 @@ interface NewTalerWireGatewayReq {
currency: 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 / * Launch Nexus and Sandbox AND creates users / facades / bank accounts /
* .. all that's required to start making banking traffic. * .. all that's required to start making banking traffic.