;(function () {
'use strict'
var request = require('request')
var _ = require('lodash')
var version = require('../package.json').version
var apiValues = require('./api.values.json')
var apiHost = apiValues.host
var methodPaths = apiValues.resources
var dateParams = ['since', 'before']
var client_id
var client_secret
/**
* @module mondo-bank
* @description
* ## Usage
*
* mondo = require('mondo-bank')
*
* All methods return a promise but can optionally be called with a callback function as the final argument
*
* #### Promise style
*
* methodPromise = mondo[$method]([$params])
* methodPromise
* .then(function(value){
* ...
* })
* .catch(function(err){
* ...
* })
*
* #### Callback style
*
* mondo[method]([$params], function(err, value){
* if (err) {
* ...
* }
* ...
* })
*/
// Helper functions
// Allow params to be aliased
function dealiasParams (params, aliases) {
params = _.extend({}, params)
Object.keys(aliases).forEach(function (param) {
var aliased = aliases[param]
aliased = typeof aliased === 'string' ? [aliased] : aliased
aliased.forEach(function (alias) {
if (params[param] === undefined && params[alias] !== undefined) {
params[param] = params[alias]
}
delete params[alias]
})
})
return params
}
// Allow params to be passed unbracketed
function bracketifyParams (obj, param) {
var bracketedObj = {}
Object.keys(obj).forEach(function (prop) {
// what if bracketed prop already exists?
bracketedObj[param + '[' + prop + ']'] = obj[prop]
})
return bracketedObj
}
// Adds necessary auth header
function addAuthorizationHeader (options, access_token) {
options.headers = options.headers || {}
options.headers.Authorization = 'Bearer ' + access_token
return options
}
function handleApiResponse (options, resolve, reject) {
request(options, function (err, res) {
if (err) {
reject(err)
} else {
var data = res.body
if (res.statusCode === 200) {
resolve(data)
} else {
reject({
status: res.statusCode,
error: data
})
}
}
})
}
// Call the API
function apiRequest (options, fn) {
options = _.extend({}, options)
if (options.form && options.form.client_id && options.form.client_secret) {
client_id = options.form.client_id
client_secret = options.form.client_secret
}
if (options.qs) {
dateParams.forEach(function (dParam) {
if (typeof options.qs[dParam] === 'object') {
options.qs[dParam] = options.qs[dParam].toISOString()
}
})
}
options.uri = apiHost + options.uri
options.json = true
options.headers = options.headers || {}
options.headers.client = 'NodeMondo-v' + version
options.method = options.method || 'GET'
if (typeof Promise === 'undefined') {
if (!fn) {
var noCallbackError = new Error('method.missing.callback')
throw noCallbackError
}
return handleApiResponse(options, function (res) {
fn(null, res)
}, fn)
}
var reqpromise = new Promise(function (resolve, reject) {
handleApiResponse(options, resolve, reject)
})
if (!fn) {
return reqpromise
} else {
reqpromise.then(function (parsed_body) {
fn(null, parsed_body)
})
.catch(function (err) {
fn(err)
})
}
}
// Call the API with authentication
function apiRequestAuthenticated (options, access_token, fn) {
options = _.extend({}, options)
options = addAuthorizationHeader(options, access_token)
return apiRequest(options, fn)
}
// API methods
/**
* @method token
* @static
* @param {object} credentials Mondo credentials
* @param {string} credentials.client_id Dev API client id
* @param {string} credentials.client_secret Dev API client secret
* @param {string} credentials.username Mondo user’s username
* @param {string} credentials.password Mondo user’s password
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Acquire an access token
*
* tokenPromise = mondo.token({
* client_id: client_id,
* client_secret: client_secret,
* username: username,
* password: password
* })
*
* @see https://getmondo.co.uk/docs/#acquiring-an-access-token
*/
function token (credentials, fn) {
var options = {
method: 'POST',
uri: methodPaths.token,
form: _.extend({
grant_type: 'password',
client_id: client_id,
client_secret: client_secret
}, credentials)
}
return apiRequest(options, fn)
}
/**
* @method tokenInfo
* @static
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Get information about an access token
*
* tokenInfoPromise = mondo.tokenInfo(accessToken)
*
* @see https://getmondo.co.uk/docs/#authenticating-requests
*/
function tokenInfo (access_token, fn) {
var options = {
uri: methodPaths.tokenInfo
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method refreshToken
* @static
* @param {string} refresh_token Refresh token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Refresh a proviously acquired token
*
* refreshTokenPromise = mondo.refreshToken(refreshToken)
*
* or if the client id and secret have not been previously passed
*
* refreshTokenPromise = mondo.refreshToken({
* refreshToken: refreshToken,
* client_id: client_id,
* client_secret: client_secret
* })
*
* @see https://getmondo.co.uk/docs/#refreshing-access
*/
function refreshToken (refresh_token, fn) {
if (typeof refresh_token === 'string') {
refresh_token = {
refresh_token: refresh_token
}
}
var options = {
method: 'POST',
uri: methodPaths.refreshToken,
form: _.extend({
grant_type: 'refresh_token',
client_id: client_id,
client_secret: client_secret
}, refresh_token)
}
return apiRequest(options, fn)
}
/**
* @method accounts
* @static
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Get detailed information about customer’s accounts
*
* accountsPromise = mondo.accounts(accessToken)
*
* @see https://getmondo.co.uk/docs/#list-accounts
*/
function accounts (access_token, fn) {
var options = {
uri: methodPaths.accounts
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method balance
* @static
* @param {string} account_id Account id
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Get balance details for an account
*
* balancePromise = mondo.balance(account_id, access_token)
*
* @see https://getmondo.co.uk/docs/#read-balance
*/
function balance (account_id, access_token, fn) {
var options = {
uri: methodPaths.balance,
qs: {
account_id: account_id
}
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method transactions
* @static
* @param {object|string} params Query object
* If passed as a string, used as account_id
* @param {string} params.account_id Account id
* @param {date|string} [params.since] Date after which to show results (Date object or ISO string)
* @param {date|string} [params.before] Date before which to show results (Date object or ISO string)
* @param {int} [params.limit] Max number of transactions to return (100 max)
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* List transactions
*
* transactionsPromise = mondo.transactions(account_id, access_token)
*
* or to filter the results
*
* transactionsPromise = mondo.transactions({
* account_id: account_id,
* since: since,
* before: before
* limit: limit
* }, access_token)
*
* @see https://getmondo.co.uk/docs/#list-transactions
*/
function transactions (params, access_token, fn) {
if (typeof params === 'string') {
params = {
account_id: params
}
}
var options = {
uri: methodPaths.transactions,
qs: params
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method transaction
* @static
* @param {object|String} params Transaction params
* @param {string} params.transaction_id Transaction ID
* @param {string} [params.expand] Property to expand (merchant)
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Get details about a transaction
*
* transactionPromise = mondo.transaction(transaction_id, access_token)
*
* or to see expanded info for the merchant
*
* transactionPromise = mondo.transaction({
* transaction_id: transaction_id,
* expand: 'merchant'
* }, access_token)
*
* @see https://getmondo.co.uk/docs/#retrieve-transaction
*/
function transaction (params, access_token, fn) {
var transaction_id
if (typeof params === 'string') {
transaction_id = params
params = undefined
} else {
transaction_id = params.transaction_id
delete params.transaction_id
}
if (params && params.expand) {
params['expand[]'] = params.expand
delete params.expand
}
var options = {
uri: methodPaths.transaction + transaction_id,
qs: params
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method annotateTransaction
* @static
* @param {string|object} transaction_id Transaction ID
* @param {string} [transaction_id.transaction_id] Transaction ID, if passed as object
* @param {string} [transaction_id.metadata] Metadata, if passed as nested object
* @param {object} metadata Annotation metadata object
* @param {object} annotation Alias for metadata
* @param {object} annotations Alias for metadata
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Annotate a transaction
*
* annotateTransactionPromise = mondo.annotateTransaction(transaction_id, {
* foo: 'bar'
* }, access_token)
*
* or
*
* annotateTransactionPromise = mondo.annotateTransaction({
* transaction_id: transaction_id,
* foo: 'bar'
* }, access_token)
*
* or
*
* annotateTransactionPromise = mondo.annotateTransaction({
* transaction_id: transaction_id,
* metadata: {
* foo: 'bar'
* }
* }, access_token)
*
* @see https://getmondo.co.uk/docs/#annotate-transaction
*/
function annotateTransaction (transaction_id, metadata, access_token, fn) {
if (typeof transaction_id === 'object') {
fn = access_token
access_token = metadata
metadata = _.extend({}, transaction_id)
transaction_id = metadata.transaction_id
delete metadata.transaction_id
metadata = dealiasParams(metadata, {
metadata: 'annotation'
})
metadata = metadata.metadata || metadata
}
metadata = bracketifyParams(metadata, 'metadata')
var options = {
method: 'PATCH',
uri: methodPaths.annotateTransaction + transaction_id,
form: metadata
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method createFeedItem
* @static
* @param {object} params Params object
* @param {string} params.account_id Account ID
* @param {object} params.params Feed item params
* @param {string} params.params.title Title for feed item
* @param {string} params.params.image_url Icon url to use as icon
* @param {string} [params.params.body] Body text to display
* @param {string} [params.params.background_color] Background colour
* @param {string} [params.params.title_color] Title colour
* @param {string} [params.params.body_color] Body colour
* @param {string} params.url Feed item url
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Publish a new feed entry
*
* createFeedItemPromise = mondo.createFeedItem({
* account_id: accountId,
* params: {
* title: title,
* image_url: image_url
* },
* url: url
* }, access_token)
*
* @see https://getmondo.co.uk/docs/#create-feed-item
*/
function createFeedItem (params, access_token, fn) {
params = _.extend({}, params)
params.type = params.type || 'basic'
var feedParams = bracketifyParams(params.params, 'params')
delete params.params
params = _.extend(params, feedParams)
var options = {
method: 'POST',
uri: methodPaths.createFeedItem,
form: params
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method registerWebhook
* @static
* @param {string} account_id Account ID
* @param {string} url Webhook url
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Register a webhook
*
* registerWebhookPromise = mondo.registerWebhook(account_id, url, access_token)
*
* See https://getmondo.co.uk/docs/#transaction-created for details of the transaction.created event which is sent to the webhook each time a new transaction is created in a user’s account
*
* @see https://getmondo.co.uk/docs/#registering-a-web-hook
*/
function registerWebhook (account_id, url, access_token, fn) {
var options = {
method: 'POST',
uri: methodPaths.registerWebhook,
form: {
account_id: account_id,
url: url
}
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method webhooks
* @static
* @param {string} account_id Account ID
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* List webhooks
*
* webhooksPromise = mondo.webhooks(account_id, access_token)
*
* @see https://getmondo.co.uk/docs/#list-web-hooks
*/
function webhooks (account_id, access_token, fn) {
var options = {
uri: methodPaths.webhooks,
qs: {
account_id: account_id
}
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method deleteWebhook
* @static
* @param {string} webhook_id Webhook ID
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Delete webhook
*
* deleteWebhookPromise = mondo.deleteWebhook(webhook_id, access_token)
*
* @see https://getmondo.co.uk/docs/#deleting-a-web-hook
*/
function deleteWebhook (webhook_id, access_token, fn) {
var options = {
method: 'DELETE',
uri: '/webhooks/' + webhook_id
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method registerAttachment
* @static
* @param {object} params Params object
* @param {string} params.external_id Transaction ID
* @param {string} params.file_type File type
* @param {string} params.file_url File url
* @param {string} [params.transaction_id] Alias for external_id
* @param {string} [params.id] Alias for external_id
* @param {string} [params.type] Alias for file_type
* @param {string} [params.url] Alias for file_url
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Register attachment
*
* registerAttachmentPromise = mondo.registerAttachment({
* external_id: transaction_id,
* file_type: file_type,
* file_url: file_url
* }, access_token)
*
* @see https://getmondo.co.uk/docs/#register-attachment
*/
function registerAttachment (params, access_token, fn) {
params = dealiasParams(params, {
external_id: ['transaction_id', 'id'],
file_type: 'type',
file_url: 'url'
})
var options = {
method: 'POST',
uri: methodPaths.registerAttachment,
form: params
}
return apiRequestAuthenticated(options, access_token, fn)
}
/**
* @method uploadAttachment
* @static
* @description
* Request upload attachment url
*
* uploadAttachmentPromise = mondo.uploadAttachment({
* file_name: file_name,
* file_type: file_type
* }, access_token)
*
* @see https://getmondo.co.uk/docs/#upload-attachment
* @param {object} params params object
* @param {string} params.file_name File name
* @param {string} params.file_type File type
* @param {string} [params.file] Alias for file_name
* @param {string} [params.name] Alias for file_name
* @param {string} [params.type] Alias for file_type
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
*/
function uploadAttachment (params, access_token, fn) {
params = dealiasParams(params, {
file_name: ['file', 'name'],
file_type: 'type'
})
var options = {
method: 'POST',
uri: methodPaths.uploadAttachment,
form: params
}
return apiRequestAuthenticated(options, access_token, fn)
/*
// rough on the train pretend code to allow upload and register in one call
if (params.transaction_id) {
// resolve apiRequest and attach image to it
var upload = apiRequest(options)
if (! fn) {
upload.then(function(parsed_body) {
fn(null, parsed_body)
})
.catch(function(err) {
fn(err)
})
} else {
return apiRequest(options, fn)
}
*/
}
/**
* @method deregisterAttachment
* @static
* @param {string} attachment_id Attachment ID
* @param {string} access_token Access token
* @param {function} [fn] Callback function
* @return {object} Response value
* @description
* Deregister attachment
*
* deregisterAttachmentPromise = mondo.deregisterAttachment(attachment_id, access_token)
*
* @see https://getmondo.co.uk/docs/#deregister-attachment
*/
function deregisterAttachment (attachment_id, access_token, fn) {
var options = {
method: 'POST',
uri: methodPaths.deregisterAttachment,
form: {
id: attachment_id
}
}
return apiRequestAuthenticated(options, access_token, fn)
}
module.exports = {
token: token,
tokenInfo: tokenInfo,
refreshToken: refreshToken,
accounts: accounts,
balance: balance,
transactions: transactions,
transaction: transaction,
annotateTransaction: annotateTransaction,
createFeedItem: createFeedItem,
webhooks: webhooks,
registerWebhook: registerWebhook,
deleteWebhook: deleteWebhook,
uploadAttachment: uploadAttachment,
registerAttachment: registerAttachment,
deregisterAttachment: deregisterAttachment
}
})()