Home Manual Reference Source Test Repository

src/provision/entities/DeviceBuilder.js

'use strict';

import ComplexBuilder from './ComplexBuilder';
import {
    SubscriberID
} from './SubscriberBuilder';
import {
    SubscriptionID
} from './SubscriptionBuilder';
import HttpStatus from 'http-status-codes';
import q from 'q';
import jp from 'jsonpath';

const ID = 'provision.device.identifier';

class BoxBuilder {
    constructor(ogapi, obj, url, key, urlParameters, timeout) {
        let _this = this;
        let subscribers = {};
        let subscriptions = {};

        this._obj = obj;
        this._url = url;
        this._ogapi = ogapi;
        this._key = key;
        this._timeout = timeout || null;
        this._objClone = Object.assign({}, obj);

        this._deviceKeys = Object.keys(obj).filter(function (dsName) {
            return dsName.indexOf('subscriber') === -1 && dsName.indexOf('subscription') === -1;
        });
        this._subscriberKeys = Object.keys(obj).filter(function (dsName) {
            return dsName.indexOf('subscriber') !== -1;
        });
        this._subscriptionKeys = Object.keys(obj).filter(function (dsName) {
            return dsName.indexOf('subscription') !== -1;
        });
        this._administrationKeys = Object.keys(obj).filter(function (dsName) {
            return dsName.indexOf('provision.administration') !== -1;
        });
        this._wrappers = [];
        this._urlParameters = urlParameters;


        this._subscriberKeys.forEach((key) => {
            _this._obj[key].forEach((value) => {
                if (!subscribers[value._index.value]) {
                    subscribers[value._index.value] = {};
                }
                subscribers[value._index.value][key] = {
                    _value: value._value
                };
            });
        });

        this._subscriptionKeys.forEach((key) => {
            _this._obj[key].forEach((value) => {
                if (!subscriptions[value._index.value]) {
                    subscriptions[value._index.value] = {};
                }
                subscriptions[value._index.value][key] = {
                    _value: value._value
                };
            });
        });

        Object.keys(subscriptions).forEach((commsId) => {
            let obj = subscriptions[commsId];
            _this._administrationKeys.forEach((key) => {
                obj[key] = _this._obj[key];
            });
            _this._wrappers.push(new WrapperBuilder(_this._ogapi, obj, _this._url.replace('devices', 'subscriptions').replace('/' + _this._key._value._current.value, ''), obj[SubscriptionID]._value._current.value));
        });
        Object.keys(subscribers).forEach((commsId) => {
            let obj = subscribers[commsId];
            _this._administrationKeys.forEach((key) => {
                obj[key] = _this._obj[key];
            });
            _this._wrappers.push(new WrapperBuilder(_this._ogapi, obj, _this._url.replace('devices', 'subscribers').replace('/' + _this._key._value._current.value, ''), obj[SubscriberID]._value._current.value));
        });
    }

    _urlWithKey() {
        return this._url + '/' + this._key._value._current.value;
    }

    _getUrlParameters() {
        return this._urlParameters;
    }

    _setUrlParameters(parameters) {
        if (this._urlParameters) {
            var keys = Object.keys(parameters);
            for (var i = 0; i < keys.length; i++) {
                var key = keys[i];
                this._urlParameters[key] = parameters[key];
            }
        } else {
            this._urlParameters = parameters;
        }
    }


    create() {
        let defer = q.defer();
        let postObj = {};
        let putObj = this._objClone;
        let childEntityPromises = [];
        let _this = this;

        Object.keys(putObj).filter((key) => {
            return key.indexOf('subscriber') === -1 && key.indexOf('subscription') === -1;
        }).forEach((deviceKey) => {
            postObj[deviceKey] = putObj[deviceKey];
        });

        this._wrappers.forEach((wrapper) => {
            childEntityPromises.push({
                wrapper: wrapper,
                promise: wrapper.execute(defer, 10)
            });
        });

        q.allSettled(
            childEntityPromises.reduce(function (previousValue, current) {
                previousValue.push(current.promise);
                return previousValue;
            }, [])).then(() => {
                defer.notify({
                    message: 'OGAPI_201_ENTITIES_CREATED',
                    type: 'success',
                    percentage: 20
                });
                defer.notify({
                    entity: _this._key._value._current.value,
                    message: 'OGAPI_CREATING_DEVICE',
                    type: 'success',
                    percentage: 25
                });
                return _this._ogapi.Napi.post(_this._url, postObj, null, null, {
                    flattened: true
                })
                    .then((res) => {
                        if (_this._wrappers.length > 0) {
                            defer.notify({
                                message: 'OGAPI_ADDING_RELATED_ENTITIES',
                                type: 'success',
                                percentage: 55
                            });
                            this._setUrlParameters({
                                'flattened': true
                            });
                            return _this._ogapi.Napi.put(_this._urlWithKey(), putObj, this._timeout, null, this._getUrlParameters())
                                .then((res) => {
                                    if (res.statusCode === HttpStatus.OK) {
                                        if (typeof _this._onCreated === "function") {
                                            _this._onCreated(res.header.location);
                                        }
                                        defer.notify({
                                            entity: _this._key._value._current.value,
                                            message: 'OGAPI_DEVICE_CREATED',
                                            type: 'success',
                                            percentage: 75
                                        });
                                        defer.resolve({
                                            location: res.header.location,
                                            statusCode: res.statusCode
                                        });
                                    } else {
                                        defer.reject({
                                            errors: res.errors,
                                            statusCode: res.statusCode
                                        });
                                    }
                                });
                        } else {
                            if (res.statusCode === HttpStatus.CREATED) {
                                if (typeof _this._onCreated === "function") {
                                    _this._onCreated(res.header.location);
                                }
                                defer.notify({
                                    entity: _this._key._value._current.value,
                                    message: 'OGAPI_DEVICE_CREATED',
                                    type: 'success',
                                    percentage: 75
                                });
                                defer.resolve({
                                    location: res.header.location,
                                    statusCode: res.statusCode
                                });
                            } else {
                                defer.reject({
                                    errors: res.errors,
                                    statusCode: res.statusCode
                                });
                            }
                        }

                    })
                    .catch((errores) => {

                        if (errores.statusCode === HttpStatus.BAD_REQUEST) {
                            let ms = jp.query(errores, '$..message')[0];

                            if (ms.includes('Entity duplicated')) {
                                defer.reject({
                                    errors: errores.data.errors,
                                    statusCode: errores.statusCode
                                });
                            } else {
                                defer.reject({
                                    errors: errores.data.errors,
                                    statusCode: errores.statusCode
                                });
                            }

                        } else {
                            defer.reject({
                                errors: errores.data,
                                statusCode: errores.statusCode
                            });
                        }
                    });
            }).catch((err) => {
                err.data.errors.forEach((err) => {
                    var error = err.description;
                    if (err.label)
                        error += ":" + err.label;
                    defer.notify({
                        message: 'Error: ' + error,
                        type: 'error',
                        percentage: 80
                    });
                });
                let deletePromises = [_this.delete(defer, 90)];
                childEntityPromises.forEach((item) => {
                    deletePromises.push(item.wrapper.delete(defer, 90));
                });
                q.allSettled(deletePromises).then(() => {
                    defer.reject(err);
                }).catch(() => {
                    defer.reject(err);
                });
            });
        return defer.promise;
    }

    update() {
        let defer = q.defer();
        let putObj = this._objClone;
        let childEntityPromises = [];
        let _this = this;

        this._wrappers.forEach((wrapper) => {
            childEntityPromises.push({
                wrapper: wrapper,
                promise: wrapper.execute(defer, 20)
            });
        });

        q.allSettled(
            childEntityPromises.reduce(function (previousValue, current) {
                previousValue.push(current.promise);
                return previousValue;
            }, [])).then(() => {
                defer.notify({
                    message: 'OGAPI_201_ENTITIES_CREATED',
                    type: 'success',
                    percentage: 40
                });
                defer.notify({
                    entity: _this._key._value._current.value,
                    message: 'OGAPI_ADDING_RELATED_ENTITIES',
                    type: 'success',
                    percentage: 45
                });
                this._setUrlParameters({
                    'flattened': true
                });

                return _this._ogapi.Napi.put(_this._url, putObj, this._timeout, null, this._getUrlParameters())
                    .then((res) => {
                        if (res.statusCode === HttpStatus.OK) {
                            if (typeof _this._onCreated === "function") {
                                _this._onCreated(res.header.location);
                            }
                            defer.notify({
                                entity: _this._key._value._current.value,
                                message: 'OGAPI_DEVICE_UPDATED',
                                type: 'success',
                                percentage: 90
                            });
                            defer.resolve({
                                location: res.header.location,
                                statusCode: res.statusCode
                            });
                        } else {
                            defer.reject({
                                errors: res.errors,
                                statusCode: res.statusCode
                            });
                        }
                    });
            }).catch((err) => {
                defer.notify('OGAPI_SOMETHING_WRONG_UPDATING_DEVICE');
                defer.reject(err);
            });
        return defer.promise;
    }

    patch() {
        let defer = q.defer();
        let putObj = this._obj;
        let childEntityPromises = [];
        let _this = this;

        this._wrappers.forEach((wrapper) => {
            childEntityPromises.push({
                wrapper: wrapper,
                promise: wrapper.execute(defer, 20)
            });
        });

        q.allSettled(
            childEntityPromises.reduce(function (previousValue, current) {
                previousValue.push(current.promise);
                return previousValue;
            }, [])).then(() => {
                defer.notify({
                    message: 'OGAPI_201_ENTITIES_CREATED',
                    type: 'success',
                    percentage: 40
                });
                defer.notify({
                    entity: _this._key._value._current.value,
                    message: 'OGAPI_ADDING_RELATED_ENTITIES',
                    type: 'success',
                    percentage: 45
                });
                this._setUrlParameters({
                    'flattened': true
                });

                return _this._ogapi.Napi.patch(_this._url, putObj, this._timeout, null, this._getUrlParameters())
                    .then((res) => {
                        if (res.statusCode === HttpStatus.OK) {
                            if (typeof _this._onCreated === "function") {
                                _this._onCreated(res.header.location);
                            }
                            defer.notify({
                                entity: _this._key._value._current.value,
                                message: 'OGAPI_DEVICE_UPDATED',
                                type: 'success',
                                percentage: 90
                            });
                            defer.resolve({
                                location: res.header.location,
                                statusCode: res.statusCode
                            });
                        } else {
                            defer.reject({
                                errors: res.errors,
                                statusCode: res.statusCode
                            });
                        }
                    });
            }).catch((err) => {
                defer.notify('OGAPI_SOMETHING_WRONG_UPDATING_DEVICE');
                defer.reject(err);
            });
        return defer.promise;
    }

    delete(defered, percentage) {
        let _this = this;
        return this._ogapi.Napi.delete(this._urlWithKey())
            .then((res) => {
                defered.notify({
                    entity: _this._key,
                    message: 'OGAPI_ENTITY_DELETED',
                    type: 'warning',
                    percentage: percentage
                });
            });
    }
}

class WrapperBuilder {
    constructor(ogapi, obj, url, key) {
        this._obj = obj;
        this._url = url;
        this._ogapi = ogapi;
        this._key = key;
        this._created = false;
    }

    _urlWithKey() {
        return this._url + '/' + this._key;
    }

    _checkExists() {
        return this._ogapi.Napi.get(this._urlWithKey()).then(function (response) {
            return response.statusCode === HttpStatus.OK;
        }).catch((err) => {
            console.warn(err);
            return false;
        });
    }


    execute(defered, percentage) {
        let defer = q.defer();
        let _this = this;
        this._checkExists().then((exists) => {
            if (!exists) {
                create(defered, defer, percentage);
            } else {
                defer.resolve({
                    message: 'OGAPI_ENTITY_ALREADY_CREATED',
                    entity: _this._key
                });
            }
        }).catch((exists) => {
            if (!exists) {
                create(defered, defer, percentage);
            } else {
                defer.resolve({
                    message: 'OGAPI_ENTITY_ALREADY_CREATED',
                    entity: _this._key
                });
            }
        });
        return defer.promise;

        function create(defered, defer, percentage) {
            _this._obj['provision.administration.serviceGroup'] = { "_value": { "_current": { "value": "emptyServiceGroup" } } };
            _this._ogapi.Napi.post(_this._url, _this._obj, null, null, {
                flattened: true
            })
                .then((res) => {
                    _this._created = true;
                    defered.notify({
                        entity: _this._key,
                        message: 'OGAPI_ENTITY_CREATED',
                        type: 'success',
                        percentage: percentage
                    });
                    defer.resolve({
                        message: 'OGAPI_ENTITY_CREATED',
                        entity: _this._key
                    });
                }).catch((err) => {
                    console.error(err);
                    defered.notify({
                        entity: _this._key,
                        message: 'OGAPI_ENTITY_CREATED',
                        type: 'warning',
                        percentage: percentage
                    });
                    defer.reject({
                        entity: _this._key,
                        message: 'OGAPI_SOMETHING_WRONG_CREATING',
                    });
                });
        }
    }

    delete(defered, percentage) {
        let _this = this;
        if (this._created) {
            return this._ogapi.Napi.delete(this._urlWithKey())
                .then((res) => {
                    defered.notify({
                        entity: _this._key,
                        message: 'OGAPI_ENTITY_DELETED',
                        type: 'warning',
                        percentage: percentage
                    });
                });
        }
        return Q.fcall(() => {
            return;
        });
    }

}

/**
 * Device builder. This builder give you the necessary tools to create a device using our OpenGate REST.
 */
export default class DeviceBuilder extends ComplexBuilder {

    /**
     * Constructor
     * @param {!InternalOpenGateAPI} ogapi - this is ogapi instance
     * @param {!string} organization - this is the organization name where device will be created
     * @param {!array} [allowedDatastreams] - Allowed datastreams to add into the new device
     * @param {!array} [definedSchemas] - Jsonschema about all OpenGate specific types
     * @param {!Validator} [jsonSchemaValidator] - Json schema validator tool
     * @param {number} ms - timeout in milliseconds    
     */

    constructor(ogapi, organization, allowedDatastreams, definedSchemas, jsonSchemaValidator, timeout) {
        super(ogapi, organization + '/devices', allowedDatastreams, definedSchemas, jsonSchemaValidator, timeout);
        this._organization = organization;
    }

    /**
     * This invoke a request to OpenGate North API and the callback is managed by promises
     * This function create a entity of provision
     * @return {Promise}
     * @property {function (result:object, statusCode:number)} then - When request it is OK
     * @property {function (error:string)} catch - When request it is NOK
     * @example
     *  ogapi.organizationsBuilder().create()
     */
    create() {
        this._checkRequiredParameters();
        return (new BoxBuilder(this._ogapi, this._composeElement(), this._resource, this._getEntityKey(), this._getUrlParameters(), this._timeout)).create();
    }

    /**
     * This invoke a request to OpenGate North API and the callback is managed by promises
     * This function updates a entity of provision and check if any subscriber/subscription exits or no. 
     * If a subscriber/subscription not exists then this entities will be created and after that will be added to entity box.
     * @return {Promise}
     * @property {function (result:object, statusCode:number)} then - When request it is OK
     * @property {function (error:string)} catch - When request it is NOK
     * @example
     *  ogapi.entityBuilder.devicesBuilder().update()
     */
    update() {
        return (new BoxBuilder(this._ogapi, this._composeElement(), this._buildURL(), this._getEntityKey(), this._getUrlParameters(), this._timeout)).update();
    }

    _getEntityKey() {
        return this._entity[ID];
    }

}