Skip to content
This repository has been archived by the owner on Sep 2, 2023. It is now read-only.

Commit

Permalink
[FEATURE] Add getNotifications API
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Cohen committed Apr 21, 2015
1 parent 6bf8e2b commit e076d62
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 49 deletions.
1 change: 1 addition & 0 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ RippleAPI.prototype = {
getOrder: Orders.getOrder,

getNotification: Notifications.getNotification,
getNotifications: Notifications.getNotifications,

wallet: Wallet,

Expand Down
54 changes: 28 additions & 26 deletions api/lib/notification_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,17 @@ NotificationParser.prototype.parse = function(notification_details, urlBase) {
hash: transaction.hash,
timestamp: '',// set below
transaction_url: '', // set below
previous_hash: notification_details.previous_hash,
previous_hash: notification_details.previous_hash || '',
previous_notification_url: '', // set below
next_hash: notification_details.next_hash,
next_hash: notification_details.next_hash || '',
next_notification_url: '', // set below
client_resource_id: notification_details.client_resource_id
};

notification.timestamp = transaction.date ?
new Date(ripple.utils.time.fromRipple(transaction.date)).toISOString() : '';

// Direction
if (account === transaction.Account) {
notification.direction = 'outgoing';
} else if (transaction.TransactionType === 'Payment'
Expand All @@ -57,44 +58,45 @@ NotificationParser.prototype.parse = function(notification_details, urlBase) {
} else {
notification.direction = 'incoming';
}

// Notification URL

if (notification.type === 'payment') {
notification.transaction_url = '/v1/accounts/' + notification.account
+ '/payments/' + (transaction.from_local_db
notification.transaction_url = urlBase + '/v1/accounts/'
+ notification.account
+ '/payments/'
+ (transaction.from_local_db
? notification.client_resource_id : notification.hash);
} else {
notification.transaction_url = '/v1/transactions/' + notification.hash;
}
if (notification.type === 'offercreate'
} else if (notification.type === 'offercreate'
|| notification.type === 'offercancel') {
notification.type = 'order';
notification.transaction_url = '/v1/accounts/' + notification.account
+ '/orders/' + notification.hash;
notification.transaction_url = urlBase + '/v1/accounts/'
+ notification.account
+ '/orders/' + notification.hash;
} else if (notification.type === 'trustset') {
notification.type = 'trustline';
notification.transaction_url = urlBase + '/v1/transactions/'
+ notification.hash;
} else if (notification.type === 'accountset') {
notification.type = 'settings';
notification.transaction_url = urlBase + '/v1/transactions/'
+ notification.hash;
}

// Next notification URL

if (next_transaction_identifier) {
notification.next_notification_url = '/v1/accounts/' +
notification.account +
'/notifications/' +
next_transaction_identifier;
notification.next_notification_url = urlBase + '/v1/accounts/'
+ notification.account + '/notifications/' + next_transaction_identifier;
}

// Previous notification URL
if (previous_transaction_identifier) {
notification.previous_notification_url = '/v1/accounts/' +
notification.account +
'/notifications/' +
previous_transaction_identifier;
notification.previous_notification_url = urlBase + '/v1/accounts/'
+ notification.account + '/notifications/'
+ previous_transaction_identifier;
}

// Add urlBase to each url in notification
Object.keys(notification).forEach(function(key) {
if (/url/.test(key) && notification[key]) {
notification[key] = urlBase +
notification[key];
}
});

return notification;
};

Expand Down
37 changes: 36 additions & 1 deletion api/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
var bignum = require('bignumber.js');
var validator = require('./schema-validator.js');
var ripple = require('ripple-lib');
var _ = require('lodash');
var async = require('async');

function wrapCatch(asyncFunction) {
return function() {
Expand Down Expand Up @@ -115,6 +117,38 @@ function isValidLedgerWord(ledger) {
return (/^current$|^closed$|^validated$/.test(ledger));
}

function attachDate(api, baseTransactions, callback) {
var groupedTx = _.groupBy(baseTransactions, function(tx) {
return tx.ledger_index;
});

function attachDateToTransactions(_groupedTx, ledger, _callback) {
api.remote.requestLedger({ledger_index: Number(ledger)},
function(err, data) {
if (err) {
return _callback(err);
}

_.each(_groupedTx[ledger], function(tx) {
tx.date = data.ledger.close_time;
});

_callback(null, _groupedTx[ledger]);
}
);
}

// TODO: Decorate _.flatten and make it an async function
async.map(_.keys(groupedTx),
_.partial(attachDateToTransactions, groupedTx),
function(err, results) {
if (err) {
return callback(err);
}
callback(null, _.flatten(results));
});
}


module.exports = {
isValidLedgerSequence: isValidLedgerSequence,
Expand All @@ -125,6 +159,7 @@ module.exports = {
parseCurrencyAmount: parseCurrencyAmount,
parseCurrencyQuery: parseCurrencyQuery,
compareTransactions: compareTransactions,
wrapCatch: wrapCatch
wrapCatch: wrapCatch,
attachDate: attachDate
};

139 changes: 122 additions & 17 deletions api/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,29 @@ function getNotificationHelper(api, account, identifier, urlBase, topCallback) {
callback(null, NotificationParser.parse(notificationDetails, urlBase));
}

function formatNotificationResponse(notificationDetails, callback) {
var responseBody = {
notification: notificationDetails
};

// Move client_resource_id to response body instead of inside
// the Notification
var client_resource_id = responseBody.notification.client_resource_id;
delete responseBody.notification.client_resource_id;
if (client_resource_id) {
responseBody.client_resource_id = client_resource_id;
}

callback(null, responseBody);
}


var steps = [
getTransaction,
checkLedger,
prepareNotificationDetails,
parseNotificationDetails
parseNotificationDetails,
formatNotificationResponse
];

async.waterfall(steps, topCallback);
Expand All @@ -241,28 +259,115 @@ function getNotification(account, identifier, urlBase, callback) {
validate.address(account);
validate.paymentIdentifier(identifier);

getNotificationHelper(this, account, identifier, urlBase,
function(error, notification) {
if (error) {
return callback(error);
}
return getNotificationHelper(this, account, identifier, urlBase, callback);
}

var responseBody = {
notification: notification
/**
* Get a notifications corresponding to the specified
* account.
*
* This function calls transactions.getAccountTransactions
* recursively to retrieve results_per_page number of transactions
* and filters the results using client-specified parameters.
*
* @param {RippleAddress} account
* @param {string} urlBase - The url to use for the transaction status URL
*
* @param {string} options.source_account
* @param {Number} options.ledger_min
* @param {Number} options.ledger_max
* @param {string} [false] options.earliest_first
* @param {string[]} options.types - @see transactions.getAccountTransactions
*
*/
// TODO: If given ledger range, check for ledger gaps
function getNotifications(account, urlBase, options, callback) {
validate.address(account);

var self = this;

function getTransactions(_callback) {

var resultsPerPage = options.results_per_page ||
transactions.DEFAULT_RESULTS_PER_PAGE;
var offset = resultsPerPage * ((options.page || 1) - 1);

var args = {
account: account,
direction: options.direction,
min: resultsPerPage,
max: resultsPerPage,
ledger_index_min: options.ledger_min,
ledger_index_max: options.ledger_max,
offset: offset,
earliestFirst: options.earliest_first
};

// Move client_resource_id to response body instead of inside
// the Notification
var client_resource_id = responseBody.notification.client_resource_id;
delete responseBody.notification.client_resource_id;
if (client_resource_id) {
responseBody.client_resource_id = client_resource_id;
transactions.getAccountTransactions(self, args, _callback);
}

// TODO: Attach date

function parseNotifications(baseTransactions, _callback) {
var numTransactions = baseTransactions.length;

function parseNotification(transaction, __callback) {
var args = {
account: account,
identifier: transaction.hash,
transaction: transaction
};

// Attaching previous and next identifiers
var idx = baseTransactions.indexOf(transaction);
var previous = baseTransactions[idx + 1];
var next = baseTransactions[idx - 1];

if (!options.earliest_first) {
args.previous_hash = previous ? previous.hash : undefined;
args.next_hash = next ? next.hash : undefined;
} else {
args.previous_hash = next ? next.hash : undefined;
args.next_hash = previous ? previous.hash : undefined;
}

args.previous_transaction_identifier = args.previous_hash;
args.next_transaction_identifier = args.next_hash;

var firstAndPaging = options.page &&
(options.earliest_first ?
args.previous_hash === undefined : args.next_hash === undefined);

var last = idx === numTransactions - 1;

if (firstAndPaging || last) {
attachPreviousAndNextTransactionIdentifiers(self, args,
function(err, _args) {
return __callback(err, NotificationParser.parse(_args, urlBase));
}
);
} else {
return __callback(null, NotificationParser.parse(args, urlBase));
}
}

callback(null, responseBody);
});
return async.map(baseTransactions, parseNotification, _callback);
}

function formatResponse(notifications, _callback) {
_callback(null, {notifications: notifications});
}

var steps = [
getTransactions,
parseNotifications,
formatResponse
];

return async.waterfall(steps, callback);
}

module.exports = {
getNotification: getNotification
getNotification: getNotification,
getNotifications: getNotifications
};
16 changes: 16 additions & 0 deletions server/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@ function getNotification(request, callback) {
api.getNotification(account, identifier, urlBase, callback);
}

function getNotifications(request, callback) {
var account = request.params.account;
var urlBase = getUrlBase(request);
// var direction = request.query.direction;
var options = {
ledger_index_min: request.query.start_ledger,
ledger_index_max: request.query.end_ledger,
earliest_first: request.query.earliest_first === 'true',
exclude_failed: request.query.exclude_failed === 'true',
results_per_page: request.query.results_per_page,
page: request.query.page
};
api.getNotifications(account, urlBase, options, callback);
}

function getBalances(request, callback) {
var account = request.params.account;
var options = {
Expand Down Expand Up @@ -308,6 +323,7 @@ module.exports = {
'/accounts/:account/orders': makeMiddleware(getOrders),
'/accounts/:account/order_book/:base/:counter': makeMiddleware(getOrderBook),
'/accounts/:account/orders/:identifier': makeMiddleware(getOrder),
'/accounts/:account/notifications': makeMiddleware(getNotifications),
'/accounts/:account/notifications/:identifier': makeMiddleware(getNotification),
'/accounts/:account/balances': makeMiddleware(getBalances),
'/accounts/:account/settings': makeMiddleware(getSettings),
Expand Down
Loading

0 comments on commit e076d62

Please sign in to comment.