Home Reference Source Test

src/services/filterBuilder.js

import ParamBuilder from './paramBuilder'
import URI from 'urijs'

export default class FilterBuilder {
  constructor () {
    this._param = new ParamBuilder(this)
    this.reset()
  }

  reset () {
    this._where = {}
    this._include = null
    this._fields = null
    this._limit = null
    this._order = null
    this._skip = null
    this._deleted = null
  }

  /**
   * Where query filter
   * Accepts operators, see [Where operators](http://docs.spaceinvoices.com/?shell#where-operators) section for details.
   * @param {*} fields
   */
  where (...fields) {
    fields.forEach(field => {
      this._where[field] = null
    })
    return this._param
  }

  /**
   * Logical AND operator
   * @param {string} fields
   */
  and (...fields) {
    this._operator('and', fields)
    return this._param
  }

  /**
   * Logical OR operator
   * @param {string} fields
   */
  or (...fields) {
    this._operator('or', fields)
    return this._param
  }

  _operator (operation, fields) {
    if (this._where[operation] === undefined) {
      let tmpWhere = Object.assign({}, this._where)
      this._where = {}
      this._where[operation] = [tmpWhere]
    }
    fields.forEach(field => {
      let tmpWhere = {}
      tmpWhere[field] = null
      this._where[operation].push(tmpWhere)
    })
  }

  /**
   * Used to include related models
   * @param {*} model
   */
  include (model) {
    this._include = model
    return this
  }

  /**
   * Include or exclude certain fields
   * @param {*} fields
   */
  fields (...fields) {
    this._fields = {}
    fields.forEach(field => {
      this._fields[field] = true
    })
    return this
  }

  /**
   * Limit amount of data returned
   * @param {number} limit
   */
  limit (limit) {
    this._limit = limit
    return this
  }

  /**
   * Order results by property
   * @param {string} field
   * @param {string} sort ASC or DESC
   */
  order (field, sort = 'ASC') {
    this._order = { field, sort }
    return this
  }

  /**
   * Number of results to skip
   * @param {number} skip
   */
  skip (skip) {
    this._skip = skip
    return this
  }

  /**
   * Also return deleted records. Only available on models that implement soft delete
   */
  deleted () {
    this._deleted = true
    return this
  }

  buildObject () {
    let filter = {}
    if (this._where) {
      filter.where = this._where
    }
    if (this._include) {
      filter.include = this._include
    }
    if (this._fields) {
      filter.fields = this._fields
    }
    if (this._limit) {
      filter.limit = this._limit
    }
    if (this._order) {
      filter.order = this._order
    }
    if (this._skip) {
      filter.skip = this._skip
    }
    if (this._deleted) {
      filter.deleted = this._deleted
    }

    return filter
  }

  buildUri (endpoint = '', decode = false) {
    let filter = { filter: this.buildObject() }

    let filterFlatten = this._flattenFilter(filter)
    let uriFilter = URI.buildQuery(filterFlatten)
    let uri = URI(endpoint + (URI(endpoint).search() ? '&' : '?') + uriFilter)

    if (decode) {
      return URI.decode(uri.toString())
    } else {
      return uri.toString()
    }
  }

  _flattenFilter (ob, lvl = 0) {
    let toReturn = {}
    for (let i in ob) {
      if (!ob.hasOwnProperty(i)) continue

      if ((typeof ob[i]) === 'object') {
        let flatObject = this._flattenFilter(ob[i], ++lvl)
        for (let x in flatObject) {
          if (!flatObject.hasOwnProperty(x)) continue

          if (lvl !== 1) {
            toReturn[`[${i}]${x}`] = flatObject[x]
          } else {
            toReturn[`${i}${x}`] = flatObject[x]
          }
        }
      } else {
        toReturn[`[${i}]`] = ob[i]
      }
    }
    return toReturn
  }
}