up-bank-api
Version:
The Up Bank API TypeScript wrapper
380 lines (366 loc) • 13.9 kB
JavaScript
import axios from 'axios';
/**
* Up API base URL.
*/
const BASE_URL = 'https://api.up.com.au/api/v1/';
/**
* Up API endpoint routes.
*/
const ENDPOINTS = {
ACCOUNTS: 'accounts',
CATEGORIES: 'categories',
TAGS: 'tags',
TRANSACTIONS: 'transactions',
WEBHOOKS: 'webhooks',
UTIL: 'util',
};
/**
* Some endpoints exist not to expose data, but to test the API itself.
* Currently there is only one endpoint in this group: ping!
*/
class UtilApi {
constructor(api) {
this.api = api;
}
/**
* Make a basic ping request to the API.
* This is useful to verify that authentication is functioning correctly.
* On authentication success an HTTP 200 status is returned.
* On failure an HTTP 401 error response is returned.
*/
ping() {
return this.api.get(`${ENDPOINTS.UTIL}/ping`);
}
}
/**
* Accounts represent the underlying store used to track balances and the transactions
* that have occurred to modify those balances over time. Up currently has two types of
* account: SAVER - used to earn interest and to hit savings goals, and
* TRANSACTIONAL - used for everyday spending.
*/
class AccountsApi {
constructor(api) {
this.api = api;
}
/**
* Retrieve a paginated list of all accounts for the currently authenticated user.
* The returned list is paginated and can be scrolled by following the prev and next links where present.
*/
list(params = {}) {
const urlParams = [];
if (params.pageSize) {
urlParams.push(`page[size]=${params.pageSize}`);
}
return this.api.get(`${ENDPOINTS.ACCOUNTS}?${urlParams.join('&')}`);
}
/**
* Retrieve a specific account by providing its unique identifier.
* @param accountId The unique identifier for the account. e.g. e7a729f0-aaa7-4d6a-b231-f794c0155e1d
*/
retrieve(accountId) {
return this.api.get(`${ENDPOINTS.ACCOUNTS}/${accountId}`);
}
}
/**
* Categories enable understanding where your money goes by driving powerful insights in Up.
* All categories in Up are pre-defined and are automatically assigned to new purchases in most cases.
* A parent-child relationship is used to represent categories, however parent categories cannot be
* directly assigned to transactions.
*/
class CategoriesApi {
constructor(api) {
this.api = api;
}
/**
* Retrieve a list of all categories and their ancestry. The returned list is not paginated.
*/
list(params = {}) {
const urlParams = [];
if (params.parent) {
urlParams.push(`filter[parent]=${params.parent}`);
}
return this.api.get(`${ENDPOINTS.CATEGORIES}?${urlParams.join('&')}`);
}
/**
* Retrieve a specific category by providing its unique identifier.
* @param categoryId The unique identifier for the category. e.g. restaurants-and-cafes
*/
retrieve(categoryId) {
return this.api.get(`${ENDPOINTS.CATEGORIES}/${categoryId}`);
}
}
class UpClient {
constructor(apiKey = null) {
this.api = null;
if (null !== apiKey) {
this.updateApiKey(apiKey);
}
}
updateApiKey(apiKey) {
this.api = axios.create({
baseURL: BASE_URL,
timeout: 5000,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
});
}
processLink(link) {
if (link) {
const linkFunc = async () => {
const parsedLink = link.slice(BASE_URL.length);
return await this.get(parsedLink);
};
linkFunc.bind(this);
return linkFunc;
}
return null;
}
async get(url) {
const res = await this.getApi().get(url);
const linksProcessObj = res.data;
/*
* If links exist, process the strings into functions that
* re-execute 'this.get()' with the new url
*/
if (linksProcessObj.links) {
linksProcessObj.links.next = this.processLink(linksProcessObj.links.next);
linksProcessObj.links.prev = this.processLink(linksProcessObj.links.prev);
}
return res.data;
}
async post(url, payload) {
const res = await this.getApi().post(url, { data: payload });
return res.data;
}
async delete(url, payload) {
const res = await this.getApi().delete(url, { data: { data: payload } });
return res.data;
}
getApi() {
if (null == this.api) {
throw new Error('You must specify an apiKey first, try calling updateApi().');
}
return this.api;
}
}
/**
* Transactions represent the movement of money into and out of an account. They have many
* characteristics that vary depending on the kind of transaction. Transactions may be temporarily
* HELD (pending) or SETTLED, typically depending on which payment method was used at the point of sale.
*/
class TransactionsApi {
constructor(api) {
this.api = api;
}
/**
* Retrieve a list of all transactions across all accounts for the currently authenticated user.
* The returned list is paginated and can be scrolled by following the next and prev links where present.
* To narrow the results to a specific date range pass one or both of filter[since] and filter[until] in
* the query string. These filter parameters should not be used for pagination. Results are ordered
* newest first to oldest last.
*/
list(params = {}) {
const urlParams = this.createUrlParams(params);
return this.api.get(`${ENDPOINTS.TRANSACTIONS}?${urlParams.join('&')}`);
}
/**
* Retrieve a specific transaction by providing its unique identifier.
* @param transactionId The unique identifier for the transaction. e.g. 58c28694-4639-4992-94f3-b030bdb06a8e
*/
retrieve(transactionId) {
return this.api.get(`${ENDPOINTS.TRANSACTIONS}/${transactionId}`);
}
/**
* Retrieve a list of all transactions for a specific account. The returned list is paginated and can be
* scrolled by following the next and prev links where present. To narrow the results to a specific date
* range pass one or both of filter[since] and filter[until] in the query string. These filter parameters
* should not be used for pagination. Results are ordered newest first to oldest last.
* @param accountId The unique identifier for the account. e.g. 7a2dfb6f-4c5c-47db-9a95-8794b1ae1470
*/
listByAccount(accountId, params = {}) {
const urlParams = this.createUrlParams(params);
return this.api.get(`/accounts/${accountId}/${ENDPOINTS.TRANSACTIONS}?${urlParams.join('&')}`);
}
createUrlParams(params) {
const urlParams = [];
if (params.pageSize) {
urlParams.push(`page[size]=${params.pageSize}`);
}
if (params.filterStatus) {
urlParams.push(`filter[status]=${params.filterStatus}`);
}
if (params.filterSince) {
urlParams.push(`filter[since]=${params.filterSince}`);
}
if (params.filterUntil) {
urlParams.push(`filter[until]=${params.filterUntil}`);
}
if (params.filterCategory) {
urlParams.push(`filter[category]=${params.filterCategory}`);
}
if (params.filterTag) {
urlParams.push(`filter[tag]=${params.filterTag}`);
}
return urlParams;
}
}
/**
* Tags are custom labels that can be associated with transactions on Up. Within
* the Up application, tags provide additional insight into spending. For
* example, you could have a "Take Away" tag that you apply to purchases from
* food delivery services. The Up API allows you to manage the tags associated
* with transactions. Each transaction may have up to 6 tags.
*/
class TagsApi {
constructor(api) {
this.api = api;
}
/**
* Retrieve a list of all tags currently in use. The returned list is not
* paginated.
*/
list(params = {}) {
const urlParams = [];
if (params.pageSize) {
urlParams.push(`page[size]=${params.pageSize}`);
}
return this.api.get(`${ENDPOINTS.TAGS}?${urlParams.join('&')}`);
}
/**
* Associates one or more tags with a specific transaction. No more than 6
* tags may be present on any single transaction. Duplicate tags are silently
* ignored.
* @param transactionId The unique identifier for the transaction. e.g.
* 0a3c4bdd-1de5-4b9b-bf9e-53fb0b5f2cd7
* @param tags The tags to add to the transaction.
*/
addTagsToTransaction(transactionId, tags) {
return this.api.post(TagsApi.buildTransactionTagsPath(transactionId), tags);
}
/**
* Disassociates one or more tags from a specific transaction. Tags that are
* not associated are silently ignored.
* @param transactionId The unique identifier for the transaction. e.g.
* 0a3c4bdd-1de5-4b9b-bf9e-53fb0b5f2cd7
* @param tags The tags to remove from the transaction.
*/
removeTagsFromTransaction(transactionId, tags) {
return this.api.delete(TagsApi.buildTransactionTagsPath(transactionId), tags);
}
/**
* Build API path to access the tags for a given transaction.
* @param transactionId The unique identifier for the transaction. e.g.
* 0a3c4bdd-1de5-4b9b-bf9e-53fb0b5f2cd7
*/
static buildTransactionTagsPath(transactionId) {
return `${ENDPOINTS.TRANSACTIONS}/${transactionId}/relationships/tags`;
}
}
/**
* Webhooks provide a mechanism for a configured URL to receive events when
* transaction activity occurs on Up. You can think of webhooks as being like
* push notifications for your server-side application.
*/
class WebhookApi {
constructor(api) {
this.api = api;
}
/**
* Retrieve a list of configured webhooks. The returned list is not
* paginated.
*/
list(params = {}) {
const urlParams = [];
if (params.pageSize) {
urlParams.push(`page[size]=${params.pageSize}`);
}
return this.api.get(`${ENDPOINTS.WEBHOOKS}?${urlParams.join('&')}`);
}
/**
* Create a new webhook with a given URL. The URL will receive webhook events
* as JSON-encoded POST requests. The URL must respond with a HTTP 200 status
* on success.
* @param url The URL that this webhook should post events to. This must be a
* valid HTTP or HTTPS URL that does not exceed 300 characters in length.
* @param description An optional description for this webhook, up to 64
* characters in length.
*/
create(url, description) {
const data = {
attributes: {
url,
description: description !== null && description !== void 0 ? description : null,
},
};
return this.api.post(ENDPOINTS.WEBHOOKS, data);
}
/**
* Retrieve a specific webhook by providing its unique identifier.
* @param id The unique identifier for the webhook. e.g.
* a3f1e92b-b790-42cf-afe7-6f4efad9fa9d
*/
retrieve(id) {
return this.api.get(`${ENDPOINTS.WEBHOOKS}/${id}`);
}
/**
* Delete a specific webhook by providing its unique identifier. Once deleted,
* webhook events will no longer be sent to the configured URL.
* @param id The unique identifier for the webhook. e.g.
* a3f1e92b-b790-42cf-afe7-6f4efad9fa9d
*/
delete(id) {
return this.api.delete(`${ENDPOINTS.WEBHOOKS}/${id}`);
}
/**
* Send a `PING` event to a webhook by providing its unique identifier. This is
* useful for testing and debugging purposes. The event is delivered
*hronously and its data is returned in the response to this request
* @param id The unique identifier for the webhook. e.g.
* a3f1e92b-b790-42cf-afe7-6f4efad9fa9d
*/
ping(id) {
return this.api.post(`${ENDPOINTS.WEBHOOKS}/${id}/ping`);
}
}
function isUpApiError(e) {
if (!(e instanceof Error)) {
return false;
}
const axiosError = e;
if (!axiosError.isAxiosError) {
return false;
}
const response = axiosError.response;
if (response === undefined) {
return false;
}
return response.data !== undefined && response.status !== undefined;
}
var AccountTypeEnum;
(function (AccountTypeEnum) {
AccountTypeEnum["saver"] = "SAVER";
AccountTypeEnum["transactional"] = "TRANSACTIONAL";
})(AccountTypeEnum || (AccountTypeEnum = {}));
var TransactionTypeEnum;
(function (TransactionTypeEnum) {
TransactionTypeEnum["held"] = "HELD";
TransactionTypeEnum["settled"] = "SETTLED";
})(TransactionTypeEnum || (TransactionTypeEnum = {}));
class UpApi {
constructor(apiKey = null) {
this.api = new UpClient(apiKey);
this.util = new UtilApi(this.api);
this.accounts = new AccountsApi(this.api);
this.categories = new CategoriesApi(this.api);
this.transactions = new TransactionsApi(this.api);
this.tags = new TagsApi(this.api);
this.webhooks = new WebhookApi(this.api);
}
updateApiKey(apiKey) {
this.api.updateApiKey(apiKey);
}
}
export { AccountTypeEnum, TransactionTypeEnum, UpApi, isUpApiError };