Home Manual Reference Source Test Repository

src/searching/builder/SearchBuilder.js

'use strict';

import Search from '../Search';

const LIMIT_START_DEF_VALUE = 1;
const LIMIT_SIZE_DEF_VALUE = 1000;

/**
 * This is a abstract class. It is a base to make all kind of search request to OpenGate North API.
 */
export default class SearchBuilder {
    /**
     * Constructor
     * @param {!InternalOpenGateAPI} parent - this is ogapi instance
     * @param {!object} routes - this defined the routes. One of those routes must be called on Builder before call build method.
     */
    constructor(parent, routes, fieldFinder) {
        if (this.constructor === SearchBuilder) {
            throw new Error("Cannot construct Abstract instances directly");
        }
        this._url = '';
        this._fieldFinder = fieldFinder;
        this._parent = parent;
        this._routes = routes;
        this._routesName = [];
        this._builderParams = {
            summary: false,
            sort: [],
            filter: {},
            limit: undefined,
            group: undefined,
            select: undefined
        };
        for (let route in this._routes) {
            this._routesName.push(route);
            this._builderParams[route] = false;
            this[route] = (_route = route) => {
                this._builderParams[_route] = true;
                return this;
            };
        }
        this._urlParams = {};
    }

    /**
     * Return a promise which it will contains an array with fields recommended with only identifier
     * @return {Promise}
     */
    findFields(input) {
        return this._fieldFinder.find(input);
    }

    /**
     * Return a promise which it will contains an array with fields recommended with complete structure
     * @return {Promise}
     */
    findAllFields(input) {
        return this._fieldFinder.findAll(input);
    }

    /**
     * Return a promise which it will contains an string with the path of a field
     * @return {Promise}
     */
    findFieldPath(field) {
        return this._fieldFinder.findFieldPath(field);
    }

    /**
     * The request will have a specific time out if it will be exceeded then the promise throw an exception
     * @example
     *  ogapi.subscriptionsSearchBuilder().withTimeout(2000) 
     * @param {number} ms - timeout in milliseconds
     * @return {SearchBuilder} 
     */
    withTimeout(ms = 2000) {
        if (typeof ms !== "number") throw new Error('Parameter ms must be a number');
        this._builderParams.timeout = ms;
        return this;
    }

    /**
     * The search request will have this filter 
     * @example
     *  ogapi.subscriptionsSearchBuilder().filter(
     *      ogapi.newFilterBuilder().and(Ex.like('prov.customid', 'SN32'), Ex.neq('entityId', '1124'))
     *  ) // Setting FilterBuilder
     *  ogapi.subscriptionsSearchBuilder().filter(
     *       {"and": [{"like": {"entityId": "0000000000000001"}}]}
     *  ) // Custom filter
     * @param {!(FilterBuilder|object)} filter 
     * @return {SearchBuilder} 
     */
    filter(filter) {
        this._builderParams.filter = (filter || {});
        return this;
    }

    /**
     * Add ascending param into the sort search object
     * @example
     *  ogapi.subscriptionsSearchBuilder().addSortAscendingBy('prov.customid') // Order by prov.customid Ascending  
     * @param {!string} filterField - This field must be allowed into the specific resource
     * @return {SearchBuilder} 
     */
    addSortAscendingBy(filterField) {
        const ASC = 'ASCENDING';
        return this.addSortBy(filterField, ASC);
    }

    /**
     * Add descending param into the sort search object 
     * @example
     *  ogapi.devicesSearchBuilder().addSortDescendingBy('prov.customid') // Order by prov.customid Descending
     * @param {!string} filterField - This field must be allowed into the specific resource
     * @return {SearchBuilder} 
     */
    addSortDescendingBy(filterField) {
        const DESC = 'DESCENDING';
        return this.addSortBy(filterField, DESC);
    }

    /**
     * Add ascending/descending param into the sort search object 
     * @example
     *  ogapi.subscriptionsSearchBuilder().addSortBy('prov.customid','ASCENDING') // Order by prov.customid Ascending
     *  ogapi.devicesSearchBuilder().addSortBy('prov.customid','DESCENDING') // Order by prov.customid Descending 
     * @param {!string} filterField - This field must be allowed into the specific resource
     * @param {!string} typeSort
     * @return {SearchBuilder} 
     */
    addSortBy(filterField, typeSort) {
        if (filterField && typeSort) {
            if (this._builderParams.sort.length > 0) {
                let ele = 0;
                let found = false;
                for (ele = 0; ele < this._builderParams.sort.length; ele++) {
                    if (this._builderParams.sort[ele].name === filterField) {
                        this._builderParams.sort[ele].type = typeSort;
                        found = true;
                    }
                }

                if (!found) this._builderParams.sort.push({
                    name: filterField,
                    type: typeSort
                });
            } else {
                this._builderParams.sort.push({
                    name: filterField,
                    type: typeSort
                });
            }
        }
        return this;
    }

    /**
     * Remove sort param from the search object 
     * @example
     *  ogapi.subscriptionsSearchBuilder().removeSortBy('prov.customid') // Remove order by prov.customid
     *  ogapi.subscriptionsSearchBuilder().removeSortBy() // Remove all order by parameters
     * @param {string} filterField - This field must be allowed into the specific resource
     * @return {SearchBuilder} 
     */
    removeSortBy(filterField) {
        if (!filterField) {
            this._builderParams.sort = [];
        } else {
            if (this._builderParams.sort.length > 0) {
                let ele = 0;
                for (ele = 0; ele < this._builderParams.sort.length; ele++) {
                    if (this._builderParams.sort[ele].name === filterField) {
                        this._builderParams.sort.splice(ele, ele + 1);
                        ele--;
                    }
                }
            }
        }

        return this;
    }

    /**
     * Set reponse pagination.
     * @example
     *  ogapi.subscribersSearchBuilder().limit(10) // Without offset
     *  ogapi.subscribersSearchBuilder().limit(25,50) //With offset value 50
     * @param {!number} size - Defined the number of elements on response
     * @param {number} [start=0] - Defined the offset on response
     * @return {SearchBuilder} 
     */
    limit(size, start = LIMIT_START_DEF_VALUE) {
        if (typeof size !== "number") throw new Error('size parameter must be a number');
        if (typeof start !== "number" || start < 1)
            start = LIMIT_START_DEF_VALUE;
        this._builderParams.limit = {
            size: size,
            start: start
        };
        return this;
    }

    /**
     * Build a instance of Search 
     *
     * @example
     *  ogapi.devicesSearchBuilder().onProvisioned().build()
     * @throws {SearchBuilderError} Throw error on url build
     * @return {Search} 
     */
    build() {
        try {

            return new Search(this._parent,
                this._buildUrl(),
                this._buildFilter(),
                this._buildLimit(),
                this._buildSort(),
                this._buildGroup(),
                this._buildSelect(),
                this._builderParams.timeout,
                this._urlParams);
        }
        catch (error) {
            console.error('!errrrror!!!!!!', error)
        }
    }

    _buildFilter() {
        let filter = this._builderParams.filter;
        if (typeof filter._filterTemplate !== "undefined")
            return filter._filterTemplate;
        return {
            filter: filter
        };
    }

    _buildSelect() {
        let select = this._builderParams.select;
        if (typeof select !== "undefined" && typeof select._selectTemplate !== "undefined")
            return select._selectTemplate;
        return {
            select: select
        };
    }
    _buildGroup() {
        let group = this._builderParams.group;
        if (typeof group !== "undefined" && typeof group._groupTemplate !== "undefined")
            return group._groupTemplate;
        return {
            group: group
        };
    }

    _buildLimit() {
        if (typeof this._builderParams.limit === "undefined")
            return {
                limit: {
                    size: LIMIT_SIZE_DEF_VALUE,
                    start: LIMIT_START_DEF_VALUE
                }
            };

        return {
            limit: this._builderParams.limit
        };
    }

    _buildSort() {
        if (this._builderParams.sort.length === 0)
            return undefined;
        return {
            sort: {
                parameters: this._builderParams.sort
            }
        };
    }

    _buildUrl() {
        if (typeof this._url !== "string" || this._url.trim().length === 0)
            throw new Error('Error on extends SearchBuilder, this._url is not defined.');
        return this._url;
    }

    _checkConstraintRoutes(isMultipleRouteEnabled = false) {
        let atLeastOneTrue = false;
        for (let route in this._routes) {
            if (atLeastOneTrue && this._builderParams[route] && !isMultipleRouteEnabled) {
                throw new Error("Must select only one of this:" + JSON.stringify(this._routesName));
            }
            atLeastOneTrue = atLeastOneTrue || this._builderParams[route];
        }
        if (!atLeastOneTrue)
            throw new Error('Must select one at least: ' + JSON.stringify(this._routesName));
    }
}