<commit_before>const assert = require('assert')
const defer = require('promise-toolbox/defer')
const pRetry = require('promise-toolbox/retry')
const { utcFormat, utcParse } = require('d3-time-format')
const { Xapi: Base } = require('xen-api')

exports.isDefaultTemplate = require('./isDefaultTemplate')

// VDI formats. (Raw is not available for delta vdi.)
exports.VDI_FORMAT_RAW = 'raw'
exports.VDI_FORMAT_VHD = 'vhd'

// Format a date (pseudo ISO 8601) from one XenServer get by
// xapi.call('host.get_servertime', host.$ref) for example
exports.formatDateTime = utcFormat('%Y%m%dT%H:%M:%SZ')

const parseDateTimeHelper = utcParse('%Y%m%dT%H:%M:%SZ')
exports.parseDateTime = function (str, defaultValue) {
  const date = parseDateTimeHelper(str)
  if (date === null) {
    if (arguments.length > 1) {
      return defaultValue
    }
    throw new RangeError(`unable to parse XAPI datetime ${JSON.stringify(str)}`)
  }
  return date.getTime()
}

const hasProps = o => {
  // eslint-disable-next-line no-unreachable-loop
  for (const key in o) {
    return true
  }
  return false
}

class Xapi extends Base {
  constructor({
    callRetryWhenTooManyPendingTasks,
    ignoreNobakVdis,
    maxUncoalescedVdis,
    vdiDestroyRetryWhenInUse,
    ...opts
  }) {
    assert.notStrictEqual(ignoreNobakVdis, undefined)

    super(opts)
    this._callRetryWhenTooManyPendingTasks = {
      delay: 5e3,
      tries: 10,
      ...callRetryWhenTooManyPendingTasks,
      when: { code: 'TOO_MANY_PENDING_TASKS' },
    }
    this._ignoreNobakVdis = ignoreNobakVdis
    this._maxUncoalescedVdis = maxUncoalescedVdis
    this._vdiDestroyRetryWhenInUse = {
      delay: 5e3,
      retries: 10,
      ...vdiDestroyRetryWhenInUse,
    }

    const genericWatchers = (this._genericWatchers = new Set())
    const objectWatchers = (this._objectWatchers = { __proto__: null })

    const onAddOrUpdate = records => {
      if (genericWatchers.size === 0 && !hasProps(objectWatchers)) {
        // no need to process records
        return
      }

      Object.keys(records).forEach(id => {
        const object = records[id]

        genericWatchers.forEach(watcher => {
          watcher(object)
        })

        if (id in objectWatchers) {
          objectWatchers[id].resolve(object)
          delete objectWatchers[id]
        }
        const ref = object.$ref
        if (ref in objectWatchers) {
          objectWatchers[ref].resolve(object)
          delete objectWatchers[ref]
        }
      })
    }
    this.objects.on('add', onAddOrUpdate)
    this.objects.on('update', onAddOrUpdate)
  }

  _waitObject(predicate) {
    if (typeof predicate === 'function') {
      const genericWatchers = this._genericWatchers

      const { promise, resolve } = defer()
      genericWatchers.add(function watcher(obj) {
        if (predicate(obj)) {
          genericWatchers.delete(watcher)
          resolve(obj)
        }
      })
      return promise
    }

    let watcher = this._objectWatchers[predicate]
    if (watcher === undefined) {
      watcher = this._objectWatchers[predicate] = defer()
    }
    return watcher.promise
  }
}
function mixin(mixins) {
  const xapiProto = Xapi.prototype
  const { defineProperties, getOwnPropertyDescriptor, getOwnPropertyNames } = Object
  const descriptors = { __proto__: null }
  Object.keys(mixins).forEach(prefix => {
    const mixinProto = mixins[prefix].prototype
    getOwnPropertyNames(mixinProto)
      .filter(_ => _ !== 'constructor')
      .forEach(name => {
        const key = name[0] === '_' ? name : `${prefix}_${name}`

        assert(!(key in descriptors), `${key} is already defined`)

        descriptors[key] = getOwnPropertyDescriptor(mixinProto, name)
      })
  })
  defineProperties(xapiProto, descriptors)
}
mixin({
  task: require('./task'),
  VBD: require('./vbd'),
  VDI: require('./vdi'),
  VIF: require('./vif'),
  VM: require('./vm'),
})
exports.Xapi = Xapi

// TODO: remove once using next promise-toolbox
function pRetryWrap(fn, options) {
  const getOptions = typeof options !== 'function' ? () => options : options
  return function () {
    return pRetry.call(() => fn.apply(this, arguments), getOptions.apply(this, arguments))
  }
}

function getCallRetryOpts() {
  return this._callRetryWhenTooManyPendingTasks
}
Xapi.prototype.call = pRetryWrap(Xapi.prototype.call, getCallRetryOpts)
Xapi.prototype.callAsync = pRetryWrap(Xapi.prototype.callAsync, getCallRetryOpts)
<commit_msg>fix(xapi/call{,Async}): fix call to retry<commit_after>const assert = require('assert')
const defer = require('promise-toolbox/defer')
const pRetry = require('promise-toolbox/retry')
const { utcFormat, utcParse } = require('d3-time-format')
const { Xapi: Base } = require('xen-api')

exports.isDefaultTemplate = require('./isDefaultTemplate')

// VDI formats. (Raw is not available for delta vdi.)
exports.VDI_FORMAT_RAW = 'raw'
exports.VDI_FORMAT_VHD = 'vhd'

// Format a date (pseudo ISO 8601) from one XenServer get by
// xapi.call('host.get_servertime', host.$ref) for example
exports.formatDateTime = utcFormat('%Y%m%dT%H:%M:%SZ')

const parseDateTimeHelper = utcParse('%Y%m%dT%H:%M:%SZ')
exports.parseDateTime = function (str, defaultValue) {
  const date = parseDateTimeHelper(str)
  if (date === null) {
    if (arguments.length > 1) {
      return defaultValue
    }
    throw new RangeError(`unable to parse XAPI datetime ${JSON.stringify(str)}`)
  }
  return date.getTime()
}

const hasProps = o => {
  // eslint-disable-next-line no-unreachable-loop
  for (const key in o) {
    return true
  }
  return false
}

class Xapi extends Base {
  constructor({
    callRetryWhenTooManyPendingTasks,
    ignoreNobakVdis,
    maxUncoalescedVdis,
    vdiDestroyRetryWhenInUse,
    ...opts
  }) {
    assert.notStrictEqual(ignoreNobakVdis, undefined)

    super(opts)
    this._callRetryWhenTooManyPendingTasks = {
      delay: 5e3,
      tries: 10,
      ...callRetryWhenTooManyPendingTasks,
      when: { code: 'TOO_MANY_PENDING_TASKS' },
    }
    this._ignoreNobakVdis = ignoreNobakVdis
    this._maxUncoalescedVdis = maxUncoalescedVdis
    this._vdiDestroyRetryWhenInUse = {
      delay: 5e3,
      retries: 10,
      ...vdiDestroyRetryWhenInUse,
    }

    const genericWatchers = (this._genericWatchers = new Set())
    const objectWatchers = (this._objectWatchers = { __proto__: null })

    const onAddOrUpdate = records => {
      if (genericWatchers.size === 0 && !hasProps(objectWatchers)) {
        // no need to process records
        return
      }

      Object.keys(records).forEach(id => {
        const object = records[id]

        genericWatchers.forEach(watcher => {
          watcher(object)
        })

        if (id in objectWatchers) {
          objectWatchers[id].resolve(object)
          delete objectWatchers[id]
        }
        const ref = object.$ref
        if (ref in objectWatchers) {
          objectWatchers[ref].resolve(object)
          delete objectWatchers[ref]
        }
      })
    }
    this.objects.on('add', onAddOrUpdate)
    this.objects.on('update', onAddOrUpdate)
  }

  _waitObject(predicate) {
    if (typeof predicate === 'function') {
      const genericWatchers = this._genericWatchers

      const { promise, resolve } = defer()
      genericWatchers.add(function watcher(obj) {
        if (predicate(obj)) {
          genericWatchers.delete(watcher)
          resolve(obj)
        }
      })
      return promise
    }

    let watcher = this._objectWatchers[predicate]
    if (watcher === undefined) {
      watcher = this._objectWatchers[predicate] = defer()
    }
    return watcher.promise
  }
}
function mixin(mixins) {
  const xapiProto = Xapi.prototype
  const { defineProperties, getOwnPropertyDescriptor, getOwnPropertyNames } = Object
  const descriptors = { __proto__: null }
  Object.keys(mixins).forEach(prefix => {
    const mixinProto = mixins[prefix].prototype
    getOwnPropertyNames(mixinProto)
      .filter(_ => _ !== 'constructor')
      .forEach(name => {
        const key = name[0] === '_' ? name : `${prefix}_${name}`

        assert(!(key in descriptors), `${key} is already defined`)

        descriptors[key] = getOwnPropertyDescriptor(mixinProto, name)
      })
  })
  defineProperties(xapiProto, descriptors)
}
mixin({
  task: require('./task'),
  VBD: require('./vbd'),
  VDI: require('./vdi'),
  VIF: require('./vif'),
  VM: require('./vm'),
})
exports.Xapi = Xapi

// TODO: remove once using next promise-toolbox
function pRetryWrap(fn, options) {
  const getOptions = typeof options !== 'function' ? () => options : options
  return function () {
    return pRetry(() => fn.apply(this, arguments), getOptions.apply(this, arguments))
  }
}

function getCallRetryOpts() {
  return this._callRetryWhenTooManyPendingTasks
}
Xapi.prototype.call = pRetryWrap(Xapi.prototype.call, getCallRetryOpts)
Xapi.prototype.callAsync = pRetryWrap(Xapi.prototype.callAsync, getCallRetryOpts)

{'fraction_non_alphanumeric': 0.12644322385382806, 'fraction_numerical': 0.003587041811456115, 'mean_word_length': 3.238479809976247, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3375052', 'n_tokens_mistral': 2847, 'n_tokens_neox': 2840, 'n_words': 584}