Source: datasources/kpn.js

define('application/datasources/kpn', [
    'rofl/datasources/abstract',
    'rofl/datasources/adapter/rest',
    'antie/application',
    'rofl/models/api/abstract',
    'rofl/lib/promise',
    'rofl/lib/utils',
    'rofl/logger',
    'application/managers/session'
], function (
    Abstract,
    RESTAdapter,
    Application,
    ModelInterface,
    Promise,
    Utils,
    Logger,
    SessionManager
) {
    'use strict';

    var app = Application.getCurrentApplication(),
        device = app.getDevice(),
        sessionManager = SessionManager.getInstance(),
        excludedModels = [
            'session',
            'parental'
        ],
        ErrorCodes = {
            USER_HAS_NO_RIGHTS: '403-10100'
        },
        isExcluded;

    isExcluded = function (modelReference) {

        if (excludedModels.indexOf(modelReference) >= 0) {
            return true;
        }

        return false;
    };

    return Abstract.extend({

        init: function (config) {
            this.setModelPath(config.modelPath);
            this.setApiEndpoint(config.endpoint.url);
            this.setAdapter(new RESTAdapter());
        },

        /**
         * Returns a Promise with an instance of the Model being requested.
         *
         * @throws {Error} When the fetched model module isn't an instance of ModelInterface.
         * @param {string|ModelInterface} modelReference - Reference to the model or instance.
         * @param {Object} opts - Options object.
         * @param {Object} opts.params - Parameters object which is used in the resolveEndpoint method of the requested Model.
         * @param {Object} [opts.data={}] - Data to send as payload.
         * @param {*} [retryFulFilled] - Resolve function for repeating request after failure.
         * @param {*} [retryRejected] - Reject function for repeating request after failure.
         * @returns {Promise} Returns a Promise.
         */
        create: function (modelReference, opts, retryFulFilled, retryRejected) {
            var self = this;

            return new Promise(function (onFulFilled, onRejected) {
                self.fetchModel(modelReference).then(function (model) {
                    var url;

                    if (!(model instanceof ModelInterface)) {
                        throw new Error('Model should be an instance of ModelInterface');
                    }

                    url = self.getApiEndpoint() + model.resolveCreateEndpoint(opts.params);

                    model.transformToCreate(opts.data).then(function (data) {
                        opts.data = data;

                        return self._adapter.create(url, opts);
                    }).then(function (response) {

                        if (model.validateCreateResponse(response)) {
                            if (retryFulFilled) {
                                return retryFulFilled(model.transformFromCreate(response));

                            }
                            return onFulFilled(model.transformFromCreate(response));
                        }

                        if (!retryFulFilled && !isExcluded(modelReference)) {
                            Logger.onApiError(url, 'POST', '200', response.message);

                            if (response.errorDescription === ErrorCodes.USER_HAS_NO_RIGHTS) {

                                return sessionManager.refreshToken()
                                    .then(function () {
                                        return self.create(modelReference, opts, onFulFilled, onRejected);
                                    });
                            }

                            // do retry once
                            return self.create(modelReference, opts, onFulFilled, onRejected);

                        }

                        if (retryRejected) {
                            return retryRejected(response || 'Invalid response from API');
                        }

                        return onRejected(response || 'Invalid response from API');
                    });
                });
            });
        },

        /**
         * Returns a Promise with an instance of the Model being requested.
         *
         * @throws {Error} When the fetched model module isn't an instance of
         *                 ModelInterface.
         * @param {string|ModelInterface} modelReference - Reference to the model or instance.
         * @param {Object} [opts={}] - Options object.
         * @param {Object} [opts.params={}] - Parameters object which is used
         *                                    for the cache key and in the
         *                                    resolveEndpoint method of the
         *                                    requested Model.
         * @param {Object} [opts.headers={}] - Headers object which is used for
         *                                     the cache key.
         * @param {boolean} [opts.notFromCache] - Whether it should get the
         *                                        model from cache if possible.
         * @param {*} [retryFulFilled] - Resolve function for repeating request after failure.
         * @param {*} [retryRejected] - Reject function for repeating request after failure.
         * @returns {Promise} Returns a Promise.
         */
        read: function (modelReference, opts, retryFulFilled, retryRejected) {
            var self = this,
                cache,
                cacheKey;

            opts = opts || {};

            // Only check for cache if we got a textual reference
            if (Utils.isString(modelReference)) {

                // Check for session cache
                cacheKey = modelReference + device.encodeJson(opts.params || {}) + device.encodeJson(opts.headers || {});

                cache = self.getCache(cacheKey);
                if (opts.notFromCache !== true && cache) {
                    return Promise.resolve(cache);
                }
            }

            // Try loading model response
            return new Promise(function (onFulFilled, onRejected) {
                self.fetchModel(modelReference).then(function (model) {
                    var responsePromise,
                        url,
                        transformedResponse;

                    if (!(model instanceof ModelInterface)) {
                        throw new Error('Model should be an instance of ModelInterface');
                    }

                    if (model.hasLocalData(opts)) {

                        // Wrap the local data in a Promise
                        responsePromise = Promise.resolve(model.getLocalData(opts));
                    } else {

                        // Load the response from a remote
                        url = self.getApiEndpoint() + model.resolveReadEndpoint(opts.params);
                        responsePromise = self._adapter.read(url, opts);
                    }

                    // Chain on either the local data or remote response
                    responsePromise.then(function (response) {

                        if (model.validateReadResponse(response)) {
                            transformedResponse = model.transformFromRead(response);

                            if (retryFulFilled) {
                                retryFulFilled(transformedResponse);
                            } else {
                                onFulFilled(transformedResponse);
                            }

                            return transformedResponse;
                        }

                        if (!retryFulFilled && !isExcluded(modelReference) && (!Utils.isFunction(model.shouldRetry) || (Utils.isFunction(model.shouldRetry) && model.shouldRetry(response)))) {
                            Logger.onApiError(url, 'GET', '200', response.message);

                            if (response.errorDescription === ErrorCodes.USER_HAS_NO_RIGHTS) {

                                return sessionManager.refreshToken()
                                    .then(function () {
                                        return self.read(modelReference, opts, onFulFilled, onRejected);
                                    });
                            }

                            // do retry once
                            return self.read(modelReference, opts, onFulFilled, onRejected);
                        }

                        if (retryRejected) {
                            retryRejected(response || 'Invalid response from API');
                        } else {
                            onRejected(response || 'Invalid response from API');
                        }
                        return Promise.reject();

                    }).then(function (transformedData) {
                        if (model.isCacheable() && cacheKey) {
                            self.setCache(cacheKey, transformedData);
                        }

                        return transformedData;
                    });
                });
            });
        },

        /**
         * Returns a Promise with an instance of the Model being requested.
         *
         * @throws {Error} When the fetched model module isn't an instance of ModelInterface.
         * @param {string|ModelInterface} modelReference - Reference to the model or instance.
         * @param {Object} opts - Options object.
         * @param {Object} opts.params - Parameters object which is used in the
         *                               resolveEndpoint method of the requested Model.
         * @param {Object} [opts.data={}] - Data to send as payload.
         * @param {*} [retryFulFilled] - Resolve function for repeating request after failure.
         * @param {*} [retryRejected] - Reject function for repeating request after failure.
         * @returns {Promise} Returns a Promise.
         */
        update: function (modelReference, opts, retryFulFilled, retryRejected) {
            var self = this;

            return new Promise(function (onFulFilled, onRejected) {

                self.fetchModel(modelReference).then(function (model) {
                    var url,
                        transformedResponse;

                    if (!(model instanceof ModelInterface)) {
                        throw new Error('Model should be an instance of ModelInterface');
                    }

                    url = self.getApiEndpoint() + model.resolveUpdateEndpoint(opts.params);

                    model.transformToUpdate(opts.data).then(function (data) {
                        opts.data = data;

                        return self._adapter.update(url, opts);
                    }).then(function (response) {

                        if (model.validateUpdateResponse(response)) {

                            transformedResponse = model.transformFromUpdate(response);

                            if (retryFulFilled) {
                                retryFulFilled(transformedResponse);
                            } else {
                                onFulFilled(transformedResponse);
                            }
                            return transformedResponse;
                        }

                        if (!retryFulFilled && !isExcluded(modelReference)) {
                            Logger.onApiError(url, 'PUT', '200', response.message);

                            if (response.errorDescription === ErrorCodes.USER_HAS_NO_RIGHTS) {

                                return sessionManager.refreshToken()
                                    .then(function () {
                                        return self.update(modelReference, opts, onFulFilled, onRejected);
                                    });
                            }

                            // do retry once
                            return self.update(modelReference, opts, onFulFilled, onRejected);
                        }

                        if (retryRejected) {
                            return retryRejected(response || 'Invalid response from API');
                        }

                        onRejected(response || 'Invalid response from API');
                    });
                });

            });
        },

        /**
         * Returns a Promise with an instance of the Model being requested.
         *
         * @throws {Error} When the fetched model module isn't an instance of ModelInterface.
         * @param {string|ModelInterface} modelReference - Reference to the model or instance.
         * @param {Object} opts - Options object.
         * @param {Object} opts.params - Parameters object which is used in the
         *                               resolveEndpoint method of the requested Model.
         * @param {*} [retryFulFilled] - Resolve function for repeating request after failure.
         * @param {*} [retryRejected] - Reject function for repeating request after failure.
         * @returns {Promise} Returns a Promise.
         */
        destroy: function (modelReference, opts, retryFulFilled, retryRejected) {
            var self = this,
                transformedResponse;

            return new Promise(function (onFulFilled, onRejected) {
                self.fetchModel(modelReference).then(function (model) {
                    var url;

                    if (!(model instanceof ModelInterface)) {
                        throw new Error('Model should be an instance of ModelInterface');
                    }

                    url = self.getApiEndpoint() + model.resolveDestroyEndpoint(opts.params);

                    self._adapter.destroy(url, opts).then(function (response) {

                        if (model.validateDestroyResponse(response)) {

                            transformedResponse = model.transformFromDestroy(response);

                            if (retryFulFilled) {
                                retryFulFilled(transformedResponse);
                            } else {
                                onFulFilled(transformedResponse);
                            }
                            return transformedResponse;
                        }

                        if (!retryFulFilled && !isExcluded(modelReference)) {
                            Logger.onApiError(url, 'DELETE', '200', response.message);

                            if (response.errorDescription === ErrorCodes.USER_HAS_NO_RIGHTS) {

                                return sessionManager.refreshToken()
                                    .then(function () {
                                        return self.destroy(modelReference, opts, onFulFilled, onRejected);
                                    });
                            }

                            // do retry once
                            return self.destroy(modelReference, opts, onFulFilled, onRejected);
                        }

                        if (retryRejected) {
                            return retryRejected(response || 'Invalid response from API');
                        }

                        return onRejected(response || 'Invalid response from API');
                    });
                });
            });
        }
    });
});