asseton
Version:
Another open banking api
157 lines (127 loc) • 4.04 kB
text/typescript
import crypto from "crypto";
import { ServerApiError } from "src/errors";
import { DatabaseTypes } from "database";
import {
IProviderAuthentication,
IProviderAuthenticationInput,
AuthenticatedProvider,
IProviderInput,
} from "../Provider";
import { NordigenClient } from "./NordigenClient";
import { NordginenApiTypes } from "./NordigenApi";
import { findProviderRequisition } from "./utils";
interface IRequisition {
id: string;
status: "CREATED" | "AUTHENTICATED";
authenticationUrl: string;
accountIDs: Array<string>;
}
export class NordigenProvider extends AuthenticatedProvider {
private client: NordigenClient;
constructor(input: IProviderInput) {
super(input);
this.client = new NordigenClient();
this.ensureToken();
}
private ensureToken = async (): Promise<void> => {
await this.client.generateToken();
};
getDataSnapshot = async (): Promise<DatabaseTypes.IProviderData> => {
const requisition = await this.getExistingRequisition();
if (!requisition || requisition.status === "CREATED") {
throw ServerApiError.AuthenticationRequired();
}
const result: DatabaseTypes.IProviderData = {
id: this.id,
createdAt: new Date().toISOString(),
accounts: [],
};
for (let accountID of requisition.accountIDs) {
const { balances }: NordginenApiTypes.IBalances = await this.client
.account(accountID)
.getBalances();
const account: DatabaseTypes.IAccount = {
assets: [],
};
balances.forEach((it) => {
account.assets.push({
symbol: it.balanceAmount.currency,
amount: Number(it.balanceAmount.amount),
});
});
result.accounts.push(account);
}
return result;
};
createAuthentication = async (
input: IProviderAuthenticationInput
): Promise<IProviderAuthentication> => {
let requisition = await this.getExistingRequisition();
if (!requisition) {
const agreement: NordginenApiTypes.IAgreement =
await this.client.createAgreement({
institutionId: this.id,
maxHistoricalDays: 30,
accessValidForDays: 1,
});
const req: NordginenApiTypes.IRequisition =
await this.client.createRequisition({
redirectUrl: input.redirectUrl,
institutionId: this.id,
reference: crypto.randomUUID(),
agreement: agreement.id,
});
requisition = this.transformRequisition(req);
}
if (requisition.status === "AUTHENTICATED") {
throw ServerApiError.AuthenticationExists();
}
return {
authenticationUrl: requisition.authenticationUrl,
};
};
deleteAuthentication = async (): Promise<void> => {
const requisition = await this.getExistingRequisition();
if (!requisition) {
throw ServerApiError.AuthenticationNotFound();
}
await this.client.deleteRequisition(requisition.id);
};
private getExistingRequisition = async (): Promise<
IRequisition | undefined
> => {
const allAgreements: NordginenApiTypes.IAgreements =
await this.client.getAgreements();
const allRequisitions: NordginenApiTypes.IRequisitions =
await this.client.getRequisitions();
const result = findProviderRequisition({
providerID: this.id,
agreements: allAgreements,
requisitions: allRequisitions,
});
if (result) {
return this.transformRequisition(result);
}
return result;
};
private transformRequisition = (
requisition: NordginenApiTypes.IRequisition
): IRequisition => {
function transformStatus(
status: NordginenApiTypes.IRequisition["status"]
): IRequisition["status"] {
switch (status) {
case "CR":
return "CREATED";
case "LN":
return "AUTHENTICATED";
}
}
return {
id: requisition.id,
authenticationUrl: requisition.link,
status: transformStatus(requisition.status),
accountIDs: requisition.accounts,
};
};
}