model/Filters.js

import { isType, checkValidRootJunction } from '../lib/check'
import { identity } from '../lib/utils'
import Filter from './Filter'

/**
 * @description
 * Collection class that contains filters that are to be applied when loading Model objects from the api.
 *
 * @memberof module:model
 */
class Filters {
    /**
     * Creates a new Filters instance.
     *
     * The Filters instance keeps a list of all the applied filters for a modelDefinition.
     *
     * @param {ModelDefinition} modelDefinition The ModelDefinition for which this Filters object should create filters.
     * @param {Filter[]} filters A list of Filter objects
     */
    constructor(modelDefinition, filters = []) {
        /**
         * @type {Array<Filter>}
         * @private
         */
        this.filters = filters
        this.modelDefinition = modelDefinition
        this.rootJunction = null
    }

    /**
     * Shortcut for triggering the creation of a Filter. This is the function that is triggered when creating new
     * filters on a ModelDefinition. The Filter will receive a callback that can be used to add the finalized filter
     * to the list of filters.
     *
     * @param {string} propertyName The property that the filter should apply to. (e.g. `name`)
     * @returns {Filter} The created filter object for `propertyName`.
     */
    on(propertyName) {
        const addFilter = this.add.bind(this)

        return Filter.getFilter(addFilter).on(propertyName)
    }

    /**
     * Utility method to add a filter to the list of filters.
     *
     * @private
     * @param {Filter} filter The Filter to be added to the list of current filters.
     * @returns {ModelDefinition} The modelDefiniton that the filter applies to. This is used to support calling `.list()`
     * on the modelDefinition after the filter was created.
     * @throws {TypeError} Thrown when the given filter is not a Filter object.
     */
    add(filter) {
        if (!isType(filter, Filter)) {
            throw new TypeError('filter should be an instance of Filter')
        }
        this.filters.push(filter)

        return this.modelDefinition
    }

    /**
     * @deprecated
     * @returns {Promise} Proxy the list() call on the filters object.
     */
    list() {
        return this.modelDefinition.list()
    }

    /**
     * Get an array of DHIS2 metadata filter values to send to the API.
     *
     * This will return ['id:eq:UYXOT4A7JMI', 'name:like:ANC'] for filters created as follows
     * dataElement
     *  .filter().on('id').equals(UYXOT4A7JMI)
     *  .filter().on('name').like('ANC')
     *
     * @returns {Array<string>} A list of query param values to be used with the filter key.
     */
    getQueryFilterValues() {
        return this.filters.map(filter => filter.getQueryParamFormat())
    }

    /**
     * @deprecated Deprecated since 2.28, use getQueryFilterValues instead.
     * @returns {Array.<string>}
     */
    getFilters() {
        return this.getQueryFilterValues()
    }

    /**
     * Get a list of Filter objects that are in applied in this Filters instance
     *
     * @returns {Array<Filter>} The list of Filter objects.
     */
    getFilterList() {
        return this.filters.map(identity)
    }

    /**
     * The logic mode to use on the filters.
     *
     * Default behavior is AND.
     * Note that the logic will be used across all the filters, which
     * means with OR, results will be returned when any of the filter match.
     * It MUST be called last on the chain of filters when called
     * through modelDefinition.filter().
     * @see {@link https://docs.dhis2.org/master/en/developer/html/webapi_metadata_object_filter.html|Object filter Docs }
     * @example
     * d2.programs.filter().on('name').like('Child')
     * .filter().logicMode('OR').on('code').equals('Child')
     * @param {string} junction The logic operator to use. One of ['OR', 'AND'];
     */
    logicMode(junction) {
        checkValidRootJunction(junction)
        this.rootJunction = junction
        return this
    }

    /**
     * Factory method to create a Filters object.
     *
     * @param {ModelDefinition} modelDefinition The modelDefinition that the filters should apply to.
     * @param {Filter[]} priorFilters List of previously applied filters that the new filter list should start with.
     * @returns {Filters} A Filters object for the given modelDefinition.
     */
    static getFilters(modelDefinition, priorFilters = []) {
        return new Filters(modelDefinition, priorFilters)
    }
}

export default Filters