all files / src/ transferUtils.js

97.83% Statements 45/46
94.44% Branches 17/18
100% Functions 8/8
97.83% Lines 45/46
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135                26×                                                                                                                                                                    
'use strict'
 
const uuid = require('node-uuid').v4
const co = require('co')
const request = require('superagent')
const https = require('https')
const agents = {}
 
/**
 * @param {Transfer} sourceTransfer
 * @param {Object} additionalInfo
 * @returns {Transfer}
 */
function setupTransferId (transfer) {
  return Object.assign({}, transfer, {
    id: transfer.id || transfer.ledger + '/transfers/' + uuid()
  })
}
 
/**
 * @param {Transfer} sourceTransfer
 * @param {Object} params
 * @param {Boolean} params.isAtomic
 * @param {Boolean} params.isUniversal
 * @param {Condition} params.executionCondition
 * @param {Condition} params.cancellationCondition (iff isAtomic)
 * @param {URI} params.caseId (iff isAtomic)
 * @returns {Transfer[]}
 */
function setupConditions (transfer, params) {
  // Use one Date.now() as the base of all expiries so that when a ms passes
  // between when the source and destination expiries are calculated the
  // minMessageWindow isn't exceeded.
  const now = Date.now()
 
  // The first transfer must be submitted by us with authorization
  transfer.debits[0].authorized = true
 
  // Add conditions/expirations to all transfers.
  Iif (params.isAtomic) {
    return setupTransferConditionsAtomic(transfer, {
      executionCondition: params.executionCondition,
      cancellationCondition: params.cancellationCondition,
      caseId: params.caseId
    })
  } else if (params.isUniversal) {
    return setupTransferConditionsUniversal(transfer, {
      executionCondition: params.executionCondition,
      now: now
    })
  } else { // optimistic mode
    delete transfer.expiry_duration
    return transfer
  }
}
 
/**
 * @param {Transfer} _transfer
 * @param {Object} params
 * @param {Condition} params.executionCondition
 * @param {Condition} params.cancellationCondition
 * @param {URI} params.caseId
 * @returns {Transfer}
 */
function setupTransferConditionsAtomic (_transfer, params) {
  const transfer = Object.assign({}, _transfer, {
    execution_condition: params.executionCondition,
    cancellation_condition: params.cancellationCondition
  })
  transfer.additional_info = transfer.additional_info || {}
  transfer.additional_info.cases = [params.caseId]
  // Atomic transfers don't expire
  // (or rather, their expiry is handled by the cancellation_condition).
  delete transfer.expiry_duration
  return transfer
}
 
/**
 * @param {Transfer} transfer
 * @param {Object} params
 * @param {Integer} params.now
 * @param {Condition} params.executionCondition
 * @returns {Transfer}
 */
function setupTransferConditionsUniversal (transfer, params) {
  transfer.expires_at = transferExpiresAt(params.now, transfer)
  transfer.execution_condition = params.executionCondition
  delete transfer.expiry_duration
  return transfer
}
 
/**
 * @param {Transfer} transfer
 * @param {Object} auth
 * @param {String} auth.username
 * @param {String} auth.password
 * @param {String|Buffer} auth.key
 * @param {String|Buffer} auth.cert
 * @param {String|Buffer} auth.ca
 * @returns {Promise<String>} the state of the transfer
 */
function postTransfer (transfer, auth) {
  return co(function * () {
    const transferReq = request.put(transfer.id)
    if (auth.username && auth.password) {
      transferReq.auth(auth.username, auth.password)
    }
    if (auth.cert || auth.ca) {
      transferReq.agent(getAgent(auth))
    }
    const transferRes = yield transferReq.send(transfer)
    return transferRes.body.state
  })
}
 
/**
 * @param {Integer} now
 * @param {Transfer} transfer
 * @returns {String} ISO-formatted date string
 */
function transferExpiresAt (now, transfer) {
  return (new Date(now + (transfer.expiry_duration * 1000))).toISOString()
}
 
function getAgent (auth) {
  return agents[auth.cert] || (agents[auth.cert] = new https.Agent(auth))
}
 
exports.setupTransferId = setupTransferId
exports.setupConditions = setupConditions
exports.setupTransferConditionsAtomic = setupTransferConditionsAtomic
exports.setupTransferConditionsUniversal = setupTransferConditionsUniversal
exports.postTransfer = postTransfer
exports.transferExpiresAt = transferExpiresAt