<commit_before>
module.exports = setup_endpoints;

var routes = require('./routes');
var identity_check = require('./identity_check');

function setup_endpoints(app) {
  var base_endpoint = '/authentication';

  /**
   * @apiDefine UserSession
   * @apiHeader {String} Cookie Cookie containing 'connect.sid', the user
   * session token.
   */

  /**
   * @apiDefine InternalError
   * @apiError (Error 500) {String} error Internal error message.
   */

  /**
   * @apiDefine IdentityValidationPost
   *
   * @apiSuccess (Success 204) status Identity validation has been initiated.
   * @apiError (Error 403) AccessDenied Access is denied.
   * @apiError (Error 400) InvalidIdentity User identity is invalid.
   * @apiError (Error 500) {String} error Internal error message.
   *
   * @apiDescription This request issue an identity validation token for the user
   * bound to the session. It sends a challenge to the email address set in the user
   * LDAP entry. The user must visit the sent URL to complete the validation and
   * continue the registration process.
   */

  /**
   * @apiDefine IdentityValidationGet
   * @apiParam {String} identity_token The one-time identity validation token provided in the email.
   * @apiSuccess (Success 200) {String} content The content of the page.
   * @apiError (Error 403) AccessDenied Access is denied.
   * @apiError (Error 500) {String} error Internal error message.
   */

  /**
   * @api {get} /authentication/login Serve login page
   * @apiName Login
   * @apiGroup Pages
   * @apiVersion 1.0.0
   *
   * @apiParam {String} redirect Redirect to this URL when user is authenticated.
   * @apiSuccess (Success 200) {String} Content The content of the login page.
   *
   * @apiDescription Create a user session and serve the login page along with
   * a cookie.
   */
  app.get  (base_endpoint + '/login',   routes.login);

  /**
   * @api {get} /authentication/logout Server logout page
   * @apiName Logout
   * @apiGroup Pages
   * @apiVersion 1.0.0
   *
   * @apiParam {String} redirect Redirect to this URL when user is deauthenticated.
   * @apiSuccess (Success 301) redirect Redirect to the URL.
   *
   * @apiDescription Deauthenticate the user and redirect him.
   */
  app.get  (base_endpoint + '/logout',  routes.logout);

  /**
   * @api {post} /authentication/totp-register Request TOTP registration
   * @apiName RequestTOTPRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationPost
   */
  /**
   * @api {get} /authentication/totp-register Serve TOTP registration page
   * @apiName ServeTOTPRegistrationPage
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationGet
   *
   *
   * @apiDescription Serves the TOTP registration page that displays the secret.
   * The secret is a QRCode and a base32 secret.
   */
  identity_check(app, base_endpoint + '/totp-register', routes.totp_register.icheck_interface);

  /**
   * @api {post} /authentication/u2f-register Request U2F registration
   * @apiName RequestU2FRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationPost
   */
  /**
   * @api {get} /authentication/u2f-register Serve U2F registration page
   * @apiName ServeU2FRegistrationPage
   * @apiGroup Pages
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationGet
   *
   * @apiDescription Serves the U2F registration page that asks the user to
   * touch the token of the U2F device.
   */
  identity_check(app, base_endpoint + '/u2f-register', routes.u2f_register.icheck_interface);

  /**
   * @api {post} /authentication/reset-password Request for password reset
   * @apiName RequestPasswordReset
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationPost
   */
  /**
   * @api {get} /authentication/reset-password Serve password reset form.
   * @apiName ServePasswordResetForm
   * @apiGroup Pages
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationGet
   *
   * @apiDescription Serves password reset form that allow the user to provide
   * the new password.
   */
  identity_check(app, base_endpoint + '/reset-password', routes.reset_password.icheck_interface);

  app.get  (base_endpoint + '/reset-password-form', function(req, res) { res.render('reset-password-form'); });

  /**
   * @api {post} /authentication/new-password Set LDAP password
   * @apiName SetLDAPPassword
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   *
   * @apiParam {String} password New password
   *
   * @apiDescription Set a new password for the user.
   */
  app.post (base_endpoint + '/new-password', routes.reset_password.post);

  /**
   * @api {post} /authentication/new-totp-secret Generate TOTP secret
   * @apiName GenerateTOTPSecret
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   *
   * @apiSuccess (Success 200) {String} base32 The base32 representation of the secret.
   * @apiSuccess (Success 200) {String} ascii The ASCII representation of the secret.
   * @apiSuccess (Success 200) {String} qrcode The QRCode of the secret in URI format.
   *
   * @apiError (Error 403) {String} error No user provided in the session or
   * unexpected identity validation challenge in the session.
   * @apiError (Error 500) {String} error Internal error message
   *
   * @apiDescription Generate a new TOTP secret and returns it.
   */
  app.post (base_endpoint + '/new-totp-secret', routes.totp_register.post);

  /**
   * @api {get} /authentication/verify Verify user authentication
   * @apiName VerifyAuthentication
   * @apiGroup Verification
   * @apiVersion 1.0.0
   * @apiUse UserSession
   *
   * @apiSuccess (Success 204) status The user is authenticated.
   * @apiError (Error 401) status The user is not authenticated.
   *
   * @apiDescription Verify that the user is authenticated, i.e., the two
   * factors have been validated
   */
  app.get  (base_endpoint + '/verify',   routes.verify);

  /**
   * @api {post} /authentication/1stfactor LDAP authentication
   * @apiName ValidateFirstFactor
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiParam {String} username User username.
   * @apiParam {String} password User password.
   *
   * @apiSuccess (Success 204) status 1st factor is validated.
   * @apiError (Error 401) {none} error 1st factor is not validated.
   * @apiError (Error 403) {none} error Access has been restricted after too
   * many authentication attempts
   *
   * @apiDescription Verify credentials against the LDAP.
   */
  app.post (base_endpoint + '/1stfactor',        routes.first_factor);

  /**
   * @api {post} /authentication/2ndfactor/totp TOTP authentication
   * @apiName ValidateTOTPSecondFactor
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiParam {String} token TOTP token.
   *
   * @apiSuccess (Success 204) status TOTP token is valid.
   * @apiError (Error 401) {none} error TOTP token is invalid.
   *
   * @apiDescription Verify TOTP token. The user is authenticated upon success.
   */
  app.post (base_endpoint + '/2ndfactor/totp',   routes.second_factor.totp);

  /**
   * @api {get} /authentication/2ndfactor/u2f/sign_request U2F Start authentication
   * @apiName StartU2FAuthentication
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 200) authentication_request The U2F authentication request.
   * @apiError (Error 401) {none} error There is no key registered for user in session.
   *
   * @apiDescription Initiate an authentication request using a U2F device.
   */
  app.get  (base_endpoint + '/2ndfactor/u2f/sign_request',       routes.second_factor.u2f.sign_request);

  /**
   * @api {post} /authentication/2ndfactor/u2f/sign U2F Complete authentication
   * @apiName CompleteU2FAuthentication
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 204) status The U2F authentication succeeded.
   * @apiError (Error 403) {none} error No authentication request has been provided.
   *
   * @apiDescription Complete authentication request of the U2F device.
   */
  app.post (base_endpoint + '/2ndfactor/u2f/sign', routes.second_factor.u2f.sign);

  /**
   * @api {get} /authentication/2ndfactor/u2f/register_request U2F Start device registration
   * @apiName StartU2FRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 200) authentication_request The U2F registration request.
   * @apiError (Error 403) {none} error Unexpected identity validation challenge.
   *
   * @apiDescription Initiate a U2F device registration request.
   */
   app.get  (base_endpoint + '/2ndfactor/u2f/register_request',   routes.second_factor.u2f.register_request);

  /**
   * @api {post} /authentication/2ndfactor/u2f/register U2F Complete device registration
   * @apiName CompleteU2FRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 204) status The U2F registration succeeded.
   * @apiError (Error 403) {none} error Unexpected identity validation challenge.
   * @apiError (Error 403) {none} error No registration request has been provided.
   *
   * @apiDescription Complete U2F registration request.
   */
   app.post (base_endpoint + '/2ndfactor/u2f/register',           routes.second_factor.u2f.register);
}

<commit_msg>Remove base url from endpoint urls<commit_after>
module.exports = setup_endpoints;

var routes = require('./routes');
var identity_check = require('./identity_check');

function setup_endpoints(app) {
  /**
   * @apiDefine UserSession
   * @apiHeader {String} Cookie Cookie containing 'connect.sid', the user
   * session token.
   */

  /**
   * @apiDefine InternalError
   * @apiError (Error 500) {String} error Internal error message.
   */

  /**
   * @apiDefine IdentityValidationPost
   *
   * @apiSuccess (Success 204) status Identity validation has been initiated.
   * @apiError (Error 403) AccessDenied Access is denied.
   * @apiError (Error 400) InvalidIdentity User identity is invalid.
   * @apiError (Error 500) {String} error Internal error message.
   *
   * @apiDescription This request issue an identity validation token for the user
   * bound to the session. It sends a challenge to the email address set in the user
   * LDAP entry. The user must visit the sent URL to complete the validation and
   * continue the registration process.
   */

  /**
   * @apiDefine IdentityValidationGet
   * @apiParam {String} identity_token The one-time identity validation token provided in the email.
   * @apiSuccess (Success 200) {String} content The content of the page.
   * @apiError (Error 403) AccessDenied Access is denied.
   * @apiError (Error 500) {String} error Internal error message.
   */

  /**
   * @api {get} /authentication/login Serve login page
   * @apiName Login
   * @apiGroup Pages
   * @apiVersion 1.0.0
   *
   * @apiParam {String} redirect Redirect to this URL when user is authenticated.
   * @apiSuccess (Success 200) {String} Content The content of the login page.
   *
   * @apiDescription Create a user session and serve the login page along with
   * a cookie.
   */
  app.get  ('/login',   routes.login);

  /**
   * @api {get} /authentication/logout Server logout page
   * @apiName Logout
   * @apiGroup Pages
   * @apiVersion 1.0.0
   *
   * @apiParam {String} redirect Redirect to this URL when user is deauthenticated.
   * @apiSuccess (Success 301) redirect Redirect to the URL.
   *
   * @apiDescription Deauthenticate the user and redirect him.
   */
  app.get  ('/logout',  routes.logout);

  /**
   * @api {post} /authentication/totp-register Request TOTP registration
   * @apiName RequestTOTPRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationPost
   */
  /**
   * @api {get} /authentication/totp-register Serve TOTP registration page
   * @apiName ServeTOTPRegistrationPage
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationGet
   *
   *
   * @apiDescription Serves the TOTP registration page that displays the secret.
   * The secret is a QRCode and a base32 secret.
   */
  identity_check(app, '/totp-register', routes.totp_register.icheck_interface);

  /**
   * @api {post} /authentication/u2f-register Request U2F registration
   * @apiName RequestU2FRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationPost
   */
  /**
   * @api {get} /authentication/u2f-register Serve U2F registration page
   * @apiName ServeU2FRegistrationPage
   * @apiGroup Pages
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationGet
   *
   * @apiDescription Serves the U2F registration page that asks the user to
   * touch the token of the U2F device.
   */
  identity_check(app, '/u2f-register', routes.u2f_register.icheck_interface);

  /**
   * @api {post} /authentication/reset-password Request for password reset
   * @apiName RequestPasswordReset
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationPost
   */
  /**
   * @api {get} /authentication/reset-password Serve password reset form.
   * @apiName ServePasswordResetForm
   * @apiGroup Pages
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse IdentityValidationGet
   *
   * @apiDescription Serves password reset form that allow the user to provide
   * the new password.
   */
  identity_check(app, '/reset-password', routes.reset_password.icheck_interface);

  app.get  ('/reset-password-form', function(req, res) { res.render('reset-password-form'); });

  /**
   * @api {post} /authentication/new-password Set LDAP password
   * @apiName SetLDAPPassword
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   *
   * @apiParam {String} password New password
   *
   * @apiDescription Set a new password for the user.
   */
  app.post ('/new-password', routes.reset_password.post);

  /**
   * @api {post} /authentication/new-totp-secret Generate TOTP secret
   * @apiName GenerateTOTPSecret
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   *
   * @apiSuccess (Success 200) {String} base32 The base32 representation of the secret.
   * @apiSuccess (Success 200) {String} ascii The ASCII representation of the secret.
   * @apiSuccess (Success 200) {String} qrcode The QRCode of the secret in URI format.
   *
   * @apiError (Error 403) {String} error No user provided in the session or
   * unexpected identity validation challenge in the session.
   * @apiError (Error 500) {String} error Internal error message
   *
   * @apiDescription Generate a new TOTP secret and returns it.
   */
  app.post ('/new-totp-secret', routes.totp_register.post);

  /**
   * @api {get} /authentication/verify Verify user authentication
   * @apiName VerifyAuthentication
   * @apiGroup Verification
   * @apiVersion 1.0.0
   * @apiUse UserSession
   *
   * @apiSuccess (Success 204) status The user is authenticated.
   * @apiError (Error 401) status The user is not authenticated.
   *
   * @apiDescription Verify that the user is authenticated, i.e., the two
   * factors have been validated
   */
  app.get  ('/verify',   routes.verify);

  /**
   * @api {post} /authentication/1stfactor LDAP authentication
   * @apiName ValidateFirstFactor
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiParam {String} username User username.
   * @apiParam {String} password User password.
   *
   * @apiSuccess (Success 204) status 1st factor is validated.
   * @apiError (Error 401) {none} error 1st factor is not validated.
   * @apiError (Error 403) {none} error Access has been restricted after too
   * many authentication attempts
   *
   * @apiDescription Verify credentials against the LDAP.
   */
  app.post ('/1stfactor',        routes.first_factor);

  /**
   * @api {post} /authentication/2ndfactor/totp TOTP authentication
   * @apiName ValidateTOTPSecondFactor
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiParam {String} token TOTP token.
   *
   * @apiSuccess (Success 204) status TOTP token is valid.
   * @apiError (Error 401) {none} error TOTP token is invalid.
   *
   * @apiDescription Verify TOTP token. The user is authenticated upon success.
   */
  app.post ('/2ndfactor/totp',   routes.second_factor.totp);

  /**
   * @api {get} /authentication/2ndfactor/u2f/sign_request U2F Start authentication
   * @apiName StartU2FAuthentication
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 200) authentication_request The U2F authentication request.
   * @apiError (Error 401) {none} error There is no key registered for user in session.
   *
   * @apiDescription Initiate an authentication request using a U2F device.
   */
  app.get  ('/2ndfactor/u2f/sign_request',       routes.second_factor.u2f.sign_request);

  /**
   * @api {post} /authentication/2ndfactor/u2f/sign U2F Complete authentication
   * @apiName CompleteU2FAuthentication
   * @apiGroup Authentication
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 204) status The U2F authentication succeeded.
   * @apiError (Error 403) {none} error No authentication request has been provided.
   *
   * @apiDescription Complete authentication request of the U2F device.
   */
  app.post ('/2ndfactor/u2f/sign', routes.second_factor.u2f.sign);

  /**
   * @api {get} /authentication/2ndfactor/u2f/register_request U2F Start device registration
   * @apiName StartU2FRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 200) authentication_request The U2F registration request.
   * @apiError (Error 403) {none} error Unexpected identity validation challenge.
   *
   * @apiDescription Initiate a U2F device registration request.
   */
   app.get  ('/2ndfactor/u2f/register_request',   routes.second_factor.u2f.register_request);

  /**
   * @api {post} /authentication/2ndfactor/u2f/register U2F Complete device registration
   * @apiName CompleteU2FRegistration
   * @apiGroup Registration
   * @apiVersion 1.0.0
   * @apiUse UserSession
   * @apiUse InternalError
   *
   * @apiSuccess (Success 204) status The U2F registration succeeded.
   * @apiError (Error 403) {none} error Unexpected identity validation challenge.
   * @apiError (Error 403) {none} error No registration request has been provided.
   *
   * @apiDescription Complete U2F registration request.
   */
   app.post ('/2ndfactor/u2f/register',           routes.second_factor.u2f.register);
}

<|endoftext|>


<commit_before>'use strict';

// external deps
var ObjectId = require('mongodb').ObjectId;

// internal deps
require('mongodb-toolkit');
var BateeqModels = require('bateeq-models');
var map = BateeqModels.map;

var ArticleApproval = BateeqModels.article.ArticleApproval;
var ArticleBrand = BateeqModels.article.ArticleBrand;
var ArticleCategory = BateeqModels.article.ArticleCategory;
var ArticleColor = BateeqModels.article.ArticleColor;
var ArticleCostCalculationDetail = BateeqModels.article.ArticleCostCalculationDetail;
var ArticleCostCalculation = BateeqModels.article.ArticleCostCalculation;
var ArticleCounter = BateeqModels.article.ArticleCounter;
var ArticleMaterial = BateeqModels.article.ArticleMaterial;
var ArticleMotif = BateeqModels.article.ArticleMotif;
var ArticleOrigin = BateeqModels.article.ArticleOrigin;
var ArticleSeason = BateeqModels.article.ArticleSeason;
var ArticleSize = BateeqModels.article.ArticleSize;
var ArticleSubCounter = BateeqModels.article.ArticleSubCounter;
var ArticleTheme = BateeqModels.article.ArticleTheme;
var ArticleType = BateeqModels.article.ArticleType;
var ArticleVariant = BateeqModels.article.ArticleVariant;
var Article = BateeqModels.article.Article;

module.exports = class ArticleCounterManager {
    constructor(db, user) {
        this.db = db;
        this.user = user;
        this.articleCounterCollection = this.db.use(map.article.ArticleCounter);
    }

    read(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

        return new Promise((resolve, reject) => {
            var deleted = {
                _deleted: false
            };
            var query = _paging.keyword ? {
                '$and': [deleted]
            } : deleted;

            if (_paging.keyword) {
                var regex = new RegExp(_paging.keyword, "i");
                var filterCode = {
                    'code': {
                        '$regex': regex
                    }
                };
                var filterName = {
                    'name': {
                        '$regex': regex
                    }
                };
                var $or = {
                    '$or': [filterCode, filterName]
                };

                query['$and'].push($or);
            }

            this.articleCounterCollection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(articleCounters => {
                    resolve(articleCounters);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getById(id) {
        return new Promise((resolve, reject) => {
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(articleCounter => {
                    resolve(articleCounter);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getSingleByQuery(query) {
        return new Promise((resolve, reject) => {
            this.articleCounterCollection
                .single(query)
                .then(articleCounter => {
                    resolve(articleCounter);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    create(articleCounter) {
        return new Promise((resolve, reject) => {
            this._validate(articleCounter)
                .then(validArticleCounter => {

                    this.articleCounterCollection.insert(validArticleCounter)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    update(articleCounter) {
        return new Promise((resolve, reject) => {
            this._validate(articleCounter)
                .then(validArticleCounter => {
                    this.articleCounterCollection.update(validArticleCounter)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    delete(articleCounter) {
        return new Promise((resolve, reject) => {
            this._validate(articleCounter)
                .then(validArticleCounter => {
                    validArticleCounter._deleted = true;
                    this.articleCounterCollection.update(validArticleCounter)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    _validate(articleCounter) {

        var errors = {};

        return new Promise((resolve, reject) => {
            var valid = new ArticleCounter(articleCounter);

            var getArticleCounter = this.articleCounterCollection.singleOrDefault({
                "$and": [{
                    _id: {
                        '$ne': valid._id
                    }
                }, {
                    code: valid.code
                }]
            });

            Promise.all([getArticleCounter])
                .then(results => {
                    var _articleCounter = results[0];

                    if (!valid.code || valid.code == '')
                        errors["code"] = "code is required";
                    else if (_articleCounter) {
                        errors["code"] = "code already exists";
                    }

                    if (!valid.name || valid.name == '')
                        errors["name"] = "name is required";

                    var itemErrors = [];
                    var itemHasError = false;
                    for (var item of valid.subCounters) {
                        var itemError = {};

                        if (!item.code || valid.code == '') {
                            itemError["code"] = "code is required";
                        }
                        else {
                            for (var i = valid.subCounters.indexOf(item) + 1; i < valid.subCounters.length; i++) {
                                var otherItem = valid.subCounters[i];
                                if (item.code == otherItem.code) {
                                    itemError["code"] = "code already exists on another sub-counter";
                                }
                            }
                        }

                        if (!item.name || valid.name == '') {
                            itemError["name"] = "name is required";
                        }
                        itemErrors.push(itemError);
                    }

                    for (var itemError of itemErrors) {
                        for (var prop in itemError) {
                            errors.subCounters = itemErrors;
                            break;
                        }
                        if (errors.subCounters)
                            break;
                    }

                    for (var prop in errors) {
                        var ValidationError = require('../../validation-error');
                        reject(new ValidationError('data does not pass validation', errors));
                    }

                    valid.stamp(this.user.username, 'manager');
                    resolve(valid);
                })
                .catch(e => {
                    reject(e);
                })
        });
    }
};<commit_msg>1.0.0 - update article-counter validation<commit_after>'use strict';

// external deps
var ObjectId = require('mongodb').ObjectId;

// internal deps
require('mongodb-toolkit');
var BateeqModels = require('bateeq-models');
var map = BateeqModels.map;

var ArticleApproval = BateeqModels.article.ArticleApproval;
var ArticleBrand = BateeqModels.article.ArticleBrand;
var ArticleCategory = BateeqModels.article.ArticleCategory;
var ArticleColor = BateeqModels.article.ArticleColor;
var ArticleCostCalculationDetail = BateeqModels.article.ArticleCostCalculationDetail;
var ArticleCostCalculation = BateeqModels.article.ArticleCostCalculation;
var ArticleCounter = BateeqModels.article.ArticleCounter;
var ArticleMaterial = BateeqModels.article.ArticleMaterial;
var ArticleMotif = BateeqModels.article.ArticleMotif;
var ArticleOrigin = BateeqModels.article.ArticleOrigin;
var ArticleSeason = BateeqModels.article.ArticleSeason;
var ArticleSize = BateeqModels.article.ArticleSize;
var ArticleSubCounter = BateeqModels.article.ArticleSubCounter;
var ArticleTheme = BateeqModels.article.ArticleTheme;
var ArticleType = BateeqModels.article.ArticleType;
var ArticleVariant = BateeqModels.article.ArticleVariant;
var Article = BateeqModels.article.Article;

module.exports = class ArticleCounterManager {
    constructor(db, user) {
        this.db = db;
        this.user = user;
        this.articleCounterCollection = this.db.use(map.article.ArticleCounter);
    }

    read(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

        return new Promise((resolve, reject) => {
            var deleted = {
                _deleted: false
            };
            var query = _paging.keyword ? {
                '$and': [deleted]
            } : deleted;

            if (_paging.keyword) {
                var regex = new RegExp(_paging.keyword, "i");
                var filterCode = {
                    'code': {
                        '$regex': regex
                    }
                };
                var filterName = {
                    'name': {
                        '$regex': regex
                    }
                };
                var $or = {
                    '$or': [filterCode, filterName]
                };

                query['$and'].push($or);
            }

            this.articleCounterCollection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(articleCounters => {
                    resolve(articleCounters);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getById(id) {
        return new Promise((resolve, reject) => {
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(articleCounter => {
                    resolve(articleCounter);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getSingleByQuery(query) {
        return new Promise((resolve, reject) => {
            this.articleCounterCollection
                .single(query)
                .then(articleCounter => {
                    resolve(articleCounter);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    create(articleCounter) {
        return new Promise((resolve, reject) => {
            this._validate(articleCounter)
                .then(validArticleCounter => {

                    this.articleCounterCollection.insert(validArticleCounter)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    update(articleCounter) {
        return new Promise((resolve, reject) => {
            this._validate(articleCounter)
                .then(validArticleCounter => {
                    this.articleCounterCollection.update(validArticleCounter)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    delete(articleCounter) {
        return new Promise((resolve, reject) => {
            this._validate(articleCounter)
                .then(validArticleCounter => {
                    validArticleCounter._deleted = true;
                    this.articleCounterCollection.update(validArticleCounter)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    _validate(articleCounter) {

        var errors = {};

        return new Promise((resolve, reject) => {
            var valid = new ArticleCounter(articleCounter);
            // 1. begin: Declare promises.
            var getArticleCounter = this.articleCounterCollection.singleOrDefault({
                "$and": [{
                    _id: {
                        '$ne': valid._id
                    }
                }, {
                    code: valid.code
                }]
            });
            // 1. end: Declare promises.

            // 2. begin: Validation.
            Promise.all([getArticleCounter])
                .then(results => {
                    var _articleCounter = results[0];

                    if (!valid.code || valid.code == '')
                        errors["code"] = "code is required";
                    else if (_articleCounter) {
                        errors["code"] = "code already exists";
                    }

                    if (!valid.name || valid.name == '')
                        errors["name"] = "name is required";

                    // 2a. begin: Validate error on item level.
                    var itemErrors = [];
                    for (var item of valid.subCounters) {
                        var itemError = {};

                        if (!item.code || item.code == '') {
                            itemError["code"] = "code is required";
                        }
                        else {
                            for (var i = valid.subCounters.indexOf(item) + 1; i < valid.subCounters.length; i++) {
                                var otherItem = valid.subCounters[i];
                                if (item.code == otherItem.code) {
                                    itemError["code"] = "code already exists on another sub-counter";
                                }
                            }
                        }

                        if (!item.name || item.name == '') {
                            itemError["name"] = "name is required";
                        }
                        itemErrors.push(itemError);
                    }
                    // 2a. end: Validate error on item level.
                    // 2b. add item level errors to parent error, if any.
                    for (var itemError of itemErrors) {
                        for (var prop in itemError) {
                            errors.subCounters = itemErrors;
                            break;
                        }
                        if (errors.subCounters)
                            break;
                    }

                    // 2c. begin: check if data has any error, reject if it has.
                    for (var prop in errors) {
                        var ValidationError = require('../../validation-error');
                        reject(new ValidationError('data does not pass validation', errors));
                    }

                    valid.stamp(this.user.username, 'manager');
                    resolve(valid);
                })
                .catch(e => {
                    reject(e);
                })
        });
    }
};<|endoftext|>


<commit_before>var cdb = require('cartodb.js');
var _ = require('underscore');
var $ = require('jquery');
var PanelWithOptionsView = require('../../editor/components/view-options/panel-with-options-view');
var StyleContentView = require('./style-content-view');
var StyleCartoCSSView = require('./style-cartocss-view');
var ScrollView = require('../../components/scroll/scroll-view');
var TabPaneView = require('../../components/tab-pane/tab-pane-view');
var TabPaneCollection = require('../../components/tab-pane/tab-pane-collection');
var Toggler = require('../../editor/components/toggler/toggler-view');
var UndoButtons = require('../../editor/components/undo-redo/undo-redo-view');

module.exports = cdb.core.View.extend({

  events: {
    'click .js-new-analysis': '_openAddAnalysis'
  },

  initialize: function (opts) {
    if (!opts.layerDefinitionsCollection) throw new Error('layersDefinitionCollection is required');
    if (!opts.layerDefinitionModel) throw new Error('layerDefinitionModel is required');
    if (!opts.querySchemaModel) throw new Error('querySchemaModel is required');
    if (!opts.modals) throw new Error('modals is required');
    if (!opts.editorModel) throw new Error('editorModel is required');

    this._layerDefinitionsCollection = opts.layerDefinitionsCollection;
    this._layerDefinitionModel = opts.layerDefinitionModel;
    this._styleModel = this._layerDefinitionModel.styleModel;
    this._modals = opts.modals;
    this._querySchemaModel = opts.querySchemaModel;
    this._editorModel = opts.editorModel;
    this._cartocssModel = this._layerDefinitionModel.cartocssModel;
    this._codemirrorModel = new cdb.core.Model({
      content: this._layerDefinitionModel.get('cartocss')
    });

    // Set edition attribute in case custom cartocss is applied
    this._editorModel.set({
      edition: !!this._layerDefinitionModel.get('cartocss_custom')
    });

    this._initBinds();
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();
    this._initViews();
    return this;
  },

  _initBinds: function () {
    this._styleModel.bind('change', function () {
      this._codemirrorModel.set('content', this._layerDefinitionModel.get('cartocss'));
    }, this);
    this.add_related_model(this._styleModel);

    this._cartocssModel.bind('undo redo', function () {
      this._codemirrorModel.set('content', this._cartocssModel.get('content'));
    }, this);
    this.add_related_model(this._cartocssModel);
  },

  _saveCartoCSS: function () {
    var content = this._codemirrorModel.get('content');

    // TODO: check if content is correct before saving it

    this._cartocssModel.set('content', content);

    this._layerDefinitionModel.save({
      cartocss_custom: true,
      cartocss: content
    });
  },

  _initViews: function () {
    var self = this;

    var tabPaneTabs = [{
      selected: !this._layerDefinitionModel.get('cartocss_custom'),
      createContentView: function () {
        return new ScrollView({
          createContentView: function () {
            return new StyleContentView({
              className: 'Editor-content',
              layerDefinitionsCollection: self._layerDefinitionsCollection,
              layerDefinitionModel: self._layerDefinitionModel,
              styleModel: self._styleModel,
              modals: self._modals,
              querySchemaModel: self._querySchemaModel,
              editorModel: self._editorModel
            });
          }
        });
      },
      createActionView: function () {
        return new UndoButtons({
          trackModel: self._styleModel,
          editorModel: self._editorModel,
          applyButton: false
        });
      }
    }, {
      selected: this._layerDefinitionModel.get('cartocss_custom'),
      createContentView: function () {
        return new StyleCartoCSSView({
          layerDefinitionModel: self._layerDefinitionModel,
          styleModel: self._styleModel,
          codemirrorModel: self._codemirrorModel,
          onApplyEvent: self._saveCartoCSS.bind(self)
        });
      },
      createActionView: function () {
        return new UndoButtons({
          trackModel: self._cartocssModel,
          editorModel: self._editorModel,
          applyButton: true,
          onApplyClick: self._saveCartoCSS.bind(self)
        });
      }
    }];

    var collectionPane = new TabPaneCollection(tabPaneTabs);
    var panelWithOptionsView = new PanelWithOptionsView({
      className: 'Editor-content',
      editorModel: self._editorModel,
      createContentView: function () {
        return new TabPaneView({
          collection: collectionPane
        });
      },
      createControlView: function () {
        return new Toggler({
          editorModel: self._editorModel,
          collection: collectionPane,
          labels: [_t('editor.style.style-toggle.values'), _t('editor.style.style-toggle.cartocss')]
        });
      },
      createActionView: function () {
        return new TabPaneView({
          collection: collectionPane,
          createContentKey: 'createActionView'
        });
      }
    });

    this.$el.append(panelWithOptionsView.render().el);
    this.addView(panelWithOptionsView);
  }

});
<commit_msg>Removed unnecessary packages<commit_after>var cdb = require('cartodb.js');
var PanelWithOptionsView = require('../../editor/components/view-options/panel-with-options-view');
var StyleContentView = require('./style-content-view');
var StyleCartoCSSView = require('./style-cartocss-view');
var ScrollView = require('../../components/scroll/scroll-view');
var TabPaneView = require('../../components/tab-pane/tab-pane-view');
var TabPaneCollection = require('../../components/tab-pane/tab-pane-collection');
var Toggler = require('../../editor/components/toggler/toggler-view');
var UndoButtons = require('../../editor/components/undo-redo/undo-redo-view');

module.exports = cdb.core.View.extend({

  events: {
    'click .js-new-analysis': '_openAddAnalysis'
  },

  initialize: function (opts) {
    if (!opts.layerDefinitionsCollection) throw new Error('layersDefinitionCollection is required');
    if (!opts.layerDefinitionModel) throw new Error('layerDefinitionModel is required');
    if (!opts.querySchemaModel) throw new Error('querySchemaModel is required');
    if (!opts.modals) throw new Error('modals is required');
    if (!opts.editorModel) throw new Error('editorModel is required');

    this._layerDefinitionsCollection = opts.layerDefinitionsCollection;
    this._layerDefinitionModel = opts.layerDefinitionModel;
    this._styleModel = this._layerDefinitionModel.styleModel;
    this._modals = opts.modals;
    this._querySchemaModel = opts.querySchemaModel;
    this._editorModel = opts.editorModel;
    this._cartocssModel = this._layerDefinitionModel.cartocssModel;
    this._codemirrorModel = new cdb.core.Model({
      content: this._layerDefinitionModel.get('cartocss')
    });

    // Set edition attribute in case custom cartocss is applied
    this._editorModel.set({
      edition: !!this._layerDefinitionModel.get('cartocss_custom')
    });

    this._initBinds();
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();
    this._initViews();
    return this;
  },

  _initBinds: function () {
    this._styleModel.bind('change', function () {
      this._codemirrorModel.set('content', this._layerDefinitionModel.get('cartocss'));
    }, this);
    this.add_related_model(this._styleModel);

    this._cartocssModel.bind('undo redo', function () {
      this._codemirrorModel.set('content', this._cartocssModel.get('content'));
    }, this);
    this.add_related_model(this._cartocssModel);
  },

  _saveCartoCSS: function () {
    var content = this._codemirrorModel.get('content');

    // TODO: check if content is correct before saving it

    this._cartocssModel.set('content', content);

    this._layerDefinitionModel.save({
      cartocss_custom: true,
      cartocss: content
    });
  },

  _initViews: function () {
    var self = this;

    var tabPaneTabs = [{
      selected: !this._layerDefinitionModel.get('cartocss_custom'),
      createContentView: function () {
        return new ScrollView({
          createContentView: function () {
            return new StyleContentView({
              className: 'Editor-content',
              layerDefinitionsCollection: self._layerDefinitionsCollection,
              layerDefinitionModel: self._layerDefinitionModel,
              styleModel: self._styleModel,
              modals: self._modals,
              querySchemaModel: self._querySchemaModel,
              editorModel: self._editorModel
            });
          }
        });
      },
      createActionView: function () {
        return new UndoButtons({
          trackModel: self._styleModel,
          editorModel: self._editorModel,
          applyButton: false
        });
      }
    }, {
      selected: this._layerDefinitionModel.get('cartocss_custom'),
      createContentView: function () {
        return new StyleCartoCSSView({
          layerDefinitionModel: self._layerDefinitionModel,
          styleModel: self._styleModel,
          codemirrorModel: self._codemirrorModel,
          onApplyEvent: self._saveCartoCSS.bind(self)
        });
      },
      createActionView: function () {
        return new UndoButtons({
          trackModel: self._cartocssModel,
          editorModel: self._editorModel,
          applyButton: true,
          onApplyClick: self._saveCartoCSS.bind(self)
        });
      }
    }];

    var collectionPane = new TabPaneCollection(tabPaneTabs);
    var panelWithOptionsView = new PanelWithOptionsView({
      className: 'Editor-content',
      editorModel: self._editorModel,
      createContentView: function () {
        return new TabPaneView({
          collection: collectionPane
        });
      },
      createControlView: function () {
        return new Toggler({
          editorModel: self._editorModel,
          collection: collectionPane,
          labels: [_t('editor.style.style-toggle.values'), _t('editor.style.style-toggle.cartocss')]
        });
      },
      createActionView: function () {
        return new TabPaneView({
          collection: collectionPane,
          createContentKey: 'createActionView'
        });
      }
    });

    this.$el.append(panelWithOptionsView.render().el);
    this.addView(panelWithOptionsView);
  }

});
<|endoftext|>


<commit_before>import { pluralize, camelize, dasherize } from '../utils/inflector';
import { toCollectionName, toModelName } from 'ember-cli-mirage/utils/normalize-name';
import Association from './associations/association';
import Collection from './collection';
import _assign from 'lodash/assign';
import _forIn from 'lodash/forIn';
import _includes from 'lodash/includes';
import assert from '../assert';

/**
  The primary use of the `Schema` class is to use it to find Models and Collections via the `Model` class methods.

  The `Schema` is most often accessed via the first parameter to a route handler:

  ```js
  this.get('posts', schema => {
    return schema.posts.where({ isAdmin: false });
  });
  ```

  It is also available from the `.schema` property of a `server` instance:

  ```js
  server.schema.users.create({ name: 'Yehuda' });
  ```

  To work with the Model or Collection returned from one of the methods below, refer to the instance methods in the API docs for the `Model` and `Collection` classes.

  @class Schema
  @constructor
  @public
 */
export default class Schema {

  constructor(db, modelsMap = {}) {
    assert(db, 'A schema requires a db');

    /**
      Returns Mirage's database. See the `Db` docs for the db's API.

      @property db
      @type {Object}
      @public
    */
    this.db = db;
    this._registry = {};
    this._dependentAssociations = { polymorphic: [] };
    this.registerModels(modelsMap);
    this.isSaving = {}; // a hash of models that are being saved, used to avoid cycles
  }

  /**
    @method registerModels
    @param hash
    @public
    @hide
   */
  registerModels(hash = {}) {
    _forIn(hash, (model, key) => {
      this.registerModel(key, hash[key]);
    });
  }

  /**
    @method registerModel
    @param type
    @param ModelClass
    @public
    @hide
   */
  registerModel(type, ModelClass) {
    let camelizedModelName = camelize(type);
    let modelName = dasherize(camelizedModelName);

    // Avoid mutating original class, because we may want to reuse it across many tests
    ModelClass = ModelClass.extend();

    // Store model & fks in registry
    // TODO: don't think this is needed anymore
    this._registry[camelizedModelName] = this._registry[camelizedModelName] || { class: null, foreignKeys: [] }; // we may have created this key before, if another model added fks to it
    this._registry[camelizedModelName].class = ModelClass;

    // TODO: set here, remove from model#constructor
    ModelClass.prototype._schema = this;
    ModelClass.prototype.modelName = modelName;
    // Set up associations
    ModelClass.prototype.hasManyAssociations = {};   // a registry of the model's hasMany associations. Key is key from model definition, value is association instance itself
    ModelClass.prototype.belongsToAssociations = {}; // a registry of the model's belongsTo associations. Key is key from model definition, value is association instance itself
    ModelClass.prototype.associationKeys = [];       // ex: address.user, user.addresses
    ModelClass.prototype.associationIdKeys = [];     // ex: address.user_id, user.address_ids
    ModelClass.prototype.dependentAssociations = []; // a registry of associations that depend on this model, needed for deletion cleanup.

    let fksAddedFromThisModel = {};
    for (let associationProperty in ModelClass.prototype) {
      if (ModelClass.prototype[associationProperty] instanceof Association) {
        let association = ModelClass.prototype[associationProperty];
        association.key = associationProperty;
        association.modelName = association.modelName || toModelName(associationProperty);
        association.ownerModelName = modelName;
        association.setSchema(this);

        // Update the registry with this association's foreign keys. This is
        // essentially our "db migration", since we must know about the fks.
        let [fkHolder, fk] = association.getForeignKeyArray();

        fksAddedFromThisModel[fkHolder] = fksAddedFromThisModel[fkHolder] || [];
        assert(
          !_includes(fksAddedFromThisModel[fkHolder], fk),
          `Your '${type}' model definition has multiple possible inverse relationships of type '${fkHolder}'.

          Please read the associations guide and specify explicit inverses: http://www.ember-cli-mirage.com/docs/v0.3.x/models/#associations`
        );
        fksAddedFromThisModel[fkHolder].push(fk);

        this._addForeignKeyToRegistry(fkHolder, fk);

        // Augment the Model's class with any methods added by this association
        association.addMethodsToModelClass(ModelClass, associationProperty);
      }
    }

    // Create a db collection for this model, if doesn't exist
    let collection = toCollectionName(modelName);
    if (!this.db[collection]) {
      this.db.createCollection(collection);
    }

    // Create the entity methods
    this[collection] = {
      camelizedModelName,
      new: (attrs) => this.new(camelizedModelName, attrs),
      create: (attrs) => this.create(camelizedModelName, attrs),
      all: (attrs) => this.all(camelizedModelName, attrs),
      find: (attrs) => this.find(camelizedModelName, attrs),
      findBy: (attrs) => this.findBy(camelizedModelName, attrs),
      findOrCreateBy: (attrs) => this.findOrCreateBy(camelizedModelName, attrs),
      where: (attrs) => this.where(camelizedModelName, attrs),
      none: (attrs) => this.none(camelizedModelName, attrs),
      first: (attrs) => this.first(camelizedModelName, attrs)
    };

    return this;
  }

  /**
    @method modelFor
    @param type
    @public
    @hide
   */
  modelFor(type) {
    return this._registry[type];
  }

  /**
    Create a new unsaved model instance with attributes *attrs*.

    ```js
    let post = blogPosts.new({ title: 'Lorem ipsum' });
    post.title;   // Lorem ipsum
    post.id;      // null
    post.isNew(); // true
    ```

    @method new
    @param type
    @param attrs
    @public
   */
  new(type, attrs) {
    return this._instantiateModel(dasherize(type), attrs);
  }

  /**
    Create a new model instance with attributes *attrs*, and insert it into the database.

    ```js
    let post = blogPosts.create({title: 'Lorem ipsum'});
    post.title;   // Lorem ipsum
    post.id;      // 1
    post.isNew(); // false
    ```

    @method create
    @param type
    @param attrs
    @public
   */
  create(type, attrs) {
    return this.new(type, attrs).save();
  }

  /**
    Return all models in the database.

    ```js
    let posts = blogPosts.all();
    // [post:1, post:2, ...]
    ```

    @method all
    @param type
    @public
   */
  all(type) {
    let collection = this._collectionForType(type);

    return this._hydrate(collection, dasherize(type));
  }

  /**
    Return an empty collection of type `type`.

    @method none
    @param type
    @public
   */
  none(type) {
    return this._hydrate([], dasherize(type));
  }

  /**
    Return one or many models in the database by id.

    ```js
    let post = blogPosts.find(1);
    let posts = blogPosts.find([1, 3, 4]);
    ```

    @method find
    @param type
    @param ids
    @public
   */
  find(type, ids) {
    let collection = this._collectionForType(type);
    let records = collection.find(ids);

    if (Array.isArray(ids)) {
      assert(
        records.length === ids.length,
        `Couldn't find all ${pluralize(type)} with ids: (${ids.join(',')}) (found ${records.length} results, but was looking for ${ids.length})`
      );
    }

    return this._hydrate(records, dasherize(type));
  }

  /**
    Returns the first model in the database that matches the key-value pairs in `attrs`. Note that a string comparison is used.

    ```js
    let post = blogPosts.findBy({ published: true });
    ```

    N.B. This will return `null` if the schema doesn't have any matching record.

    @method findBy
    @param type
    @param attributeName
    @public
   */
  findBy(type, query) {
    let collection = this._collectionForType(type);
    let record = collection.findBy(query);

    return this._hydrate(record, dasherize(type));
  }

  /**
    Returns the first model in the database that matches the key-value pairs in `attrs`, or creates a record with the attributes if one is not found.

    ```js
    // Find the first published blog post, or create a new one.
    let post = blogPosts.findBy({ published: true });
    ```

    @method findOrCreateBy
    @param type
    @param attributeName
    @public
   */
  findOrCreateBy(type, attrs) {
    let collection = this._collectionForType(type);
    let record = collection.findBy(attrs);
    let model;

    if (!record) {
      model = this.create(type, attrs);
    } else {
      model = this._hydrate(record, dasherize(type));
    }

    return model;
  }

  /**
    Return an array of models in the database matching the key-value pairs in *query*. Note that a string comparison is used.

    ```js
    let posts = blogPosts.where({ published: true });
    ```

    @method where
    @param type
    @param query
    @public
   */
  where(type, query) {
    let collection = this._collectionForType(type);
    let records = collection.where(query);

    return this._hydrate(records, dasherize(type));
  }

  /**
    Returns the first model in the database.

    ```js
    let post = blogPosts.first();
    ```

    N.B. This will return `null` if the schema doesn't contain any records.

    @method first
    @param type
    @public
   */
  first(type) {
    let collection = this._collectionForType(type);
    let record = collection[0];

    return this._hydrate(record, dasherize(type));
  }

  /**
    @method modelClassFor
    @param modelName
    @public
    @hide
   */
  modelClassFor(modelName) {
    let model = this._registry[camelize(modelName)];

    assert(model, `Model not registered: ${modelName}`);

    return model.class.prototype;
  }

  /*
    This method updates the dependentAssociations registry, which is used to
    keep track of which models depend on a given association. It's used when
    deleting models - their dependents need to be looked up and foreign keys
    updated.

    For example,

        schema = {
          post: Model.extend(),
          comment: Model.extend({
            post: belongsTo()
          })
        };

        comment1.post = post1;
        ...
        post1.destroy()

    Deleting this post should clear out comment1's foreign key.

    Polymorphic associations can have _any_ other model as a dependent, so we
    handle them separately.
  */
  addDependentAssociation(association, modelName) {
    if (association.isPolymorphic) {
      this._dependentAssociations.polymorphic.push(association);
    } else {
      this._dependentAssociations[modelName] = this._dependentAssociations[modelName] || [];
      this._dependentAssociations[modelName].push(association);
    }
  }

  dependentAssociationsFor(modelName) {
    let directDependents = this._dependentAssociations[modelName] || [];
    let polymorphicAssociations = this._dependentAssociations.polymorphic || [];

    return directDependents.concat(polymorphicAssociations);
  }

  associationsFor(modelName) {
    let modelClass = this.modelClassFor(modelName);

    return _assign({}, modelClass.belongsToAssociations, modelClass.hasManyAssociations);
  }

  hasModelForModelName(modelName) {
    return this.modelFor(camelize(modelName));
  }

  /*
    Private methods
  */

  /**
    @method _collectionForType
    @param type
    @private
    @hide
   */
  _collectionForType(type) {
    let collection = toCollectionName(type);
    assert(
      this.db[collection],
      `You're trying to find model(s) of type ${type} but this collection doesn't exist in the database.`
    );

    return this.db[collection];
  }

  /**
    @method _addForeignKeyToRegistry
    @param type
    @param fk
    @private
    @hide
   */
  _addForeignKeyToRegistry(type, fk) {
    this._registry[type] = this._registry[type] || { class: null, foreignKeys: [] };

    let fks = this._registry[type].foreignKeys;
    if (!_includes(fks, fk)) {
      fks.push(fk);
    }
  }

  /**
    @method _instantiateModel
    @param modelName
    @param attrs
    @private
    @hide
   */
  _instantiateModel(modelName, attrs) {
    let ModelClass = this._modelFor(modelName);
    let fks = this._foreignKeysFor(modelName);

    return new ModelClass(this, modelName, attrs, fks);
  }

  /**
    @method _modelFor
    @param modelName
    @private
    @hide
   */
  _modelFor(modelName) {
    return this._registry[camelize(modelName)].class;
  }

  /**
    @method _foreignKeysFor
    @param modelName
    @private
    @hide
   */
  _foreignKeysFor(modelName) {
    return this._registry[camelize(modelName)].foreignKeys;
  }

  /**
    Takes a record and returns a model, or an array of records
    and returns a collection.
   *
    @method _hydrate
    @param records
    @param modelName
    @private
    @hide
   */
  _hydrate(records, modelName) {
    if (Array.isArray(records)) {
      let models = records.map(function(record) {
        return this._instantiateModel(modelName, record);
      }, this);
      return new Collection(modelName, models);
    } else if (records) {
      return this._instantiateModel(modelName, records);
    } else {
      return null;
    }
  }
}
<commit_msg>Docs: Fix where the  docs reference wrong method (#1615)<commit_after>import { pluralize, camelize, dasherize } from '../utils/inflector';
import { toCollectionName, toModelName } from 'ember-cli-mirage/utils/normalize-name';
import Association from './associations/association';
import Collection from './collection';
import _assign from 'lodash/assign';
import _forIn from 'lodash/forIn';
import _includes from 'lodash/includes';
import assert from '../assert';

/**
  The primary use of the `Schema` class is to use it to find Models and Collections via the `Model` class methods.

  The `Schema` is most often accessed via the first parameter to a route handler:

  ```js
  this.get('posts', schema => {
    return schema.posts.where({ isAdmin: false });
  });
  ```

  It is also available from the `.schema` property of a `server` instance:

  ```js
  server.schema.users.create({ name: 'Yehuda' });
  ```

  To work with the Model or Collection returned from one of the methods below, refer to the instance methods in the API docs for the `Model` and `Collection` classes.

  @class Schema
  @constructor
  @public
 */
export default class Schema {

  constructor(db, modelsMap = {}) {
    assert(db, 'A schema requires a db');

    /**
      Returns Mirage's database. See the `Db` docs for the db's API.

      @property db
      @type {Object}
      @public
    */
    this.db = db;
    this._registry = {};
    this._dependentAssociations = { polymorphic: [] };
    this.registerModels(modelsMap);
    this.isSaving = {}; // a hash of models that are being saved, used to avoid cycles
  }

  /**
    @method registerModels
    @param hash
    @public
    @hide
   */
  registerModels(hash = {}) {
    _forIn(hash, (model, key) => {
      this.registerModel(key, hash[key]);
    });
  }

  /**
    @method registerModel
    @param type
    @param ModelClass
    @public
    @hide
   */
  registerModel(type, ModelClass) {
    let camelizedModelName = camelize(type);
    let modelName = dasherize(camelizedModelName);

    // Avoid mutating original class, because we may want to reuse it across many tests
    ModelClass = ModelClass.extend();

    // Store model & fks in registry
    // TODO: don't think this is needed anymore
    this._registry[camelizedModelName] = this._registry[camelizedModelName] || { class: null, foreignKeys: [] }; // we may have created this key before, if another model added fks to it
    this._registry[camelizedModelName].class = ModelClass;

    // TODO: set here, remove from model#constructor
    ModelClass.prototype._schema = this;
    ModelClass.prototype.modelName = modelName;
    // Set up associations
    ModelClass.prototype.hasManyAssociations = {};   // a registry of the model's hasMany associations. Key is key from model definition, value is association instance itself
    ModelClass.prototype.belongsToAssociations = {}; // a registry of the model's belongsTo associations. Key is key from model definition, value is association instance itself
    ModelClass.prototype.associationKeys = [];       // ex: address.user, user.addresses
    ModelClass.prototype.associationIdKeys = [];     // ex: address.user_id, user.address_ids
    ModelClass.prototype.dependentAssociations = []; // a registry of associations that depend on this model, needed for deletion cleanup.

    let fksAddedFromThisModel = {};
    for (let associationProperty in ModelClass.prototype) {
      if (ModelClass.prototype[associationProperty] instanceof Association) {
        let association = ModelClass.prototype[associationProperty];
        association.key = associationProperty;
        association.modelName = association.modelName || toModelName(associationProperty);
        association.ownerModelName = modelName;
        association.setSchema(this);

        // Update the registry with this association's foreign keys. This is
        // essentially our "db migration", since we must know about the fks.
        let [fkHolder, fk] = association.getForeignKeyArray();

        fksAddedFromThisModel[fkHolder] = fksAddedFromThisModel[fkHolder] || [];
        assert(
          !_includes(fksAddedFromThisModel[fkHolder], fk),
          `Your '${type}' model definition has multiple possible inverse relationships of type '${fkHolder}'.

          Please read the associations guide and specify explicit inverses: http://www.ember-cli-mirage.com/docs/v0.3.x/models/#associations`
        );
        fksAddedFromThisModel[fkHolder].push(fk);

        this._addForeignKeyToRegistry(fkHolder, fk);

        // Augment the Model's class with any methods added by this association
        association.addMethodsToModelClass(ModelClass, associationProperty);
      }
    }

    // Create a db collection for this model, if doesn't exist
    let collection = toCollectionName(modelName);
    if (!this.db[collection]) {
      this.db.createCollection(collection);
    }

    // Create the entity methods
    this[collection] = {
      camelizedModelName,
      new: (attrs) => this.new(camelizedModelName, attrs),
      create: (attrs) => this.create(camelizedModelName, attrs),
      all: (attrs) => this.all(camelizedModelName, attrs),
      find: (attrs) => this.find(camelizedModelName, attrs),
      findBy: (attrs) => this.findBy(camelizedModelName, attrs),
      findOrCreateBy: (attrs) => this.findOrCreateBy(camelizedModelName, attrs),
      where: (attrs) => this.where(camelizedModelName, attrs),
      none: (attrs) => this.none(camelizedModelName, attrs),
      first: (attrs) => this.first(camelizedModelName, attrs)
    };

    return this;
  }

  /**
    @method modelFor
    @param type
    @public
    @hide
   */
  modelFor(type) {
    return this._registry[type];
  }

  /**
    Create a new unsaved model instance with attributes *attrs*.

    ```js
    let post = blogPosts.new({ title: 'Lorem ipsum' });
    post.title;   // Lorem ipsum
    post.id;      // null
    post.isNew(); // true
    ```

    @method new
    @param type
    @param attrs
    @public
   */
  new(type, attrs) {
    return this._instantiateModel(dasherize(type), attrs);
  }

  /**
    Create a new model instance with attributes *attrs*, and insert it into the database.

    ```js
    let post = blogPosts.create({title: 'Lorem ipsum'});
    post.title;   // Lorem ipsum
    post.id;      // 1
    post.isNew(); // false
    ```

    @method create
    @param type
    @param attrs
    @public
   */
  create(type, attrs) {
    return this.new(type, attrs).save();
  }

  /**
    Return all models in the database.

    ```js
    let posts = blogPosts.all();
    // [post:1, post:2, ...]
    ```

    @method all
    @param type
    @public
   */
  all(type) {
    let collection = this._collectionForType(type);

    return this._hydrate(collection, dasherize(type));
  }

  /**
    Return an empty collection of type `type`.

    @method none
    @param type
    @public
   */
  none(type) {
    return this._hydrate([], dasherize(type));
  }

  /**
    Return one or many models in the database by id.

    ```js
    let post = blogPosts.find(1);
    let posts = blogPosts.find([1, 3, 4]);
    ```

    @method find
    @param type
    @param ids
    @public
   */
  find(type, ids) {
    let collection = this._collectionForType(type);
    let records = collection.find(ids);

    if (Array.isArray(ids)) {
      assert(
        records.length === ids.length,
        `Couldn't find all ${pluralize(type)} with ids: (${ids.join(',')}) (found ${records.length} results, but was looking for ${ids.length})`
      );
    }

    return this._hydrate(records, dasherize(type));
  }

  /**
    Returns the first model in the database that matches the key-value pairs in `attrs`. Note that a string comparison is used.

    ```js
    let post = blogPosts.findBy({ published: true });
    ```

    N.B. This will return `null` if the schema doesn't have any matching record.

    @method findBy
    @param type
    @param attributeName
    @public
   */
  findBy(type, query) {
    let collection = this._collectionForType(type);
    let record = collection.findBy(query);

    return this._hydrate(record, dasherize(type));
  }

  /**
    Returns the first model in the database that matches the key-value pairs in `attrs`, or creates a record with the attributes if one is not found.

    ```js
    // Find the first published blog post, or create a new one.
    let post = blogPosts.findOrCreateBy({ published: true });
    ```

    @method findOrCreateBy
    @param type
    @param attributeName
    @public
   */
  findOrCreateBy(type, attrs) {
    let collection = this._collectionForType(type);
    let record = collection.findBy(attrs);
    let model;

    if (!record) {
      model = this.create(type, attrs);
    } else {
      model = this._hydrate(record, dasherize(type));
    }

    return model;
  }

  /**
    Return an array of models in the database matching the key-value pairs in *query*. Note that a string comparison is used.

    ```js
    let posts = blogPosts.where({ published: true });
    ```

    @method where
    @param type
    @param query
    @public
   */
  where(type, query) {
    let collection = this._collectionForType(type);
    let records = collection.where(query);

    return this._hydrate(records, dasherize(type));
  }

  /**
    Returns the first model in the database.

    ```js
    let post = blogPosts.first();
    ```

    N.B. This will return `null` if the schema doesn't contain any records.

    @method first
    @param type
    @public
   */
  first(type) {
    let collection = this._collectionForType(type);
    let record = collection[0];

    return this._hydrate(record, dasherize(type));
  }

  /**
    @method modelClassFor
    @param modelName
    @public
    @hide
   */
  modelClassFor(modelName) {
    let model = this._registry[camelize(modelName)];

    assert(model, `Model not registered: ${modelName}`);

    return model.class.prototype;
  }

  /*
    This method updates the dependentAssociations registry, which is used to
    keep track of which models depend on a given association. It's used when
    deleting models - their dependents need to be looked up and foreign keys
    updated.

    For example,

        schema = {
          post: Model.extend(),
          comment: Model.extend({
            post: belongsTo()
          })
        };

        comment1.post = post1;
        ...
        post1.destroy()

    Deleting this post should clear out comment1's foreign key.

    Polymorphic associations can have _any_ other model as a dependent, so we
    handle them separately.
  */
  addDependentAssociation(association, modelName) {
    if (association.isPolymorphic) {
      this._dependentAssociations.polymorphic.push(association);
    } else {
      this._dependentAssociations[modelName] = this._dependentAssociations[modelName] || [];
      this._dependentAssociations[modelName].push(association);
    }
  }

  dependentAssociationsFor(modelName) {
    let directDependents = this._dependentAssociations[modelName] || [];
    let polymorphicAssociations = this._dependentAssociations.polymorphic || [];

    return directDependents.concat(polymorphicAssociations);
  }

  associationsFor(modelName) {
    let modelClass = this.modelClassFor(modelName);

    return _assign({}, modelClass.belongsToAssociations, modelClass.hasManyAssociations);
  }

  hasModelForModelName(modelName) {
    return this.modelFor(camelize(modelName));
  }

  /*
    Private methods
  */

  /**
    @method _collectionForType
    @param type
    @private
    @hide
   */
  _collectionForType(type) {
    let collection = toCollectionName(type);
    assert(
      this.db[collection],
      `You're trying to find model(s) of type ${type} but this collection doesn't exist in the database.`
    );

    return this.db[collection];
  }

  /**
    @method _addForeignKeyToRegistry
    @param type
    @param fk
    @private
    @hide
   */
  _addForeignKeyToRegistry(type, fk) {
    this._registry[type] = this._registry[type] || { class: null, foreignKeys: [] };

    let fks = this._registry[type].foreignKeys;
    if (!_includes(fks, fk)) {
      fks.push(fk);
    }
  }

  /**
    @method _instantiateModel
    @param modelName
    @param attrs
    @private
    @hide
   */
  _instantiateModel(modelName, attrs) {
    let ModelClass = this._modelFor(modelName);
    let fks = this._foreignKeysFor(modelName);

    return new ModelClass(this, modelName, attrs, fks);
  }

  /**
    @method _modelFor
    @param modelName
    @private
    @hide
   */
  _modelFor(modelName) {
    return this._registry[camelize(modelName)].class;
  }

  /**
    @method _foreignKeysFor
    @param modelName
    @private
    @hide
   */
  _foreignKeysFor(modelName) {
    return this._registry[camelize(modelName)].foreignKeys;
  }

  /**
    Takes a record and returns a model, or an array of records
    and returns a collection.
   *
    @method _hydrate
    @param records
    @param modelName
    @private
    @hide
   */
  _hydrate(records, modelName) {
    if (Array.isArray(records)) {
      let models = records.map(function(record) {
        return this._instantiateModel(modelName, record);
      }, this);
      return new Collection(modelName, models);
    } else if (records) {
      return this._instantiateModel(modelName, records);
    } else {
      return null;
    }
  }
}
<|endoftext|>


<commit_before>define('app/controllers/images', [
    'app/models/image',
    'ember',
    'jquery'
    ],
    /**
     * Images controller
     *
     *
     * @returns Class
     */
    function(Image) {
        return Ember.ArrayController.extend({
            backend: null,

            getImage: function(id, callback) {
                // Linode will pass null, so don't bother
                if (id == null){
                    return false;
                }

                var foundImage = false;
                if (this.content) {
                    $.each(this.content, function(idx, image) {
                        if (image.id == id) {
                            foundImage = true;
                            return image;
                        }
                    });
                }

                if (!foundImage) {
                    return false
                }
            },

            init: function() {
                this._super();

                var that = this;
                $.getJSON('/backends/' + this.backend.index + '/images', function(data) {
                    var content = new Array();
                    data.forEach(function(item){
                        item.backend = that.backend;
                        content.push(Image.create(item));
                    });
                    that.set('content', content);
                }).error(function() {
                    Mist.notificationController.notify("Error loading images for backend: " + that.backend.title);
                });
            }
        });
    }
);
<commit_msg>return found image<commit_after>define('app/controllers/images', [
    'app/models/image',
    'ember',
    'jquery'
    ],
    /**
     * Images controller
     *
     *
     * @returns Class
     */
    function(Image) {
        return Ember.ArrayController.extend({
            backend: null,

            getImage: function(id, callback) {
                // Linode will pass null, so don't bother
                if (id == null){
                    return false;
                }

                var foundImage = false;
                if (this.content) {
                    $.each(this.content, function(idx, image) {
                        if (image.id == id) {
                            foundImage = image;
                            return false;
                        }
                    });
                }

                return foundImage;
            },

            init: function() {
                this._super();

                var that = this;
                $.getJSON('/backends/' + this.backend.index + '/images', function(data) {
                    var content = new Array();
                    data.forEach(function(item){
                        item.backend = that.backend;
                        content.push(Image.create(item));
                    });
                    that.set('content', content);
                }).error(function() {
                    Mist.notificationController.notify("Error loading images for backend: " + that.backend.title);
                });
            }
        });
    }
);
<|endoftext|>


<commit_before>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import Errors from 'ui/utils/errors';
import layout from './template';
import { get, set, observer, computed, setProperties } from '@ember/object';
import { on } from '@ember/object/evented';

export default Component.extend({
  layout,
  tagName:     'section',
  classNames:  ['well'],
  settings:    service(),
  access:      service(),
  github:      service(),
  intl:        service(),

  model:       null,
  individuals: 'siteAccess.users',
  collection:  'siteAccess.groups',
  principals:  null,

  saved:       true,
  errors:      null,

  actions: {
    addAuthorized(principal) {
      if ( !principal ) {
        return;
      }

      this.send('clearError');

      set(this, 'saved', false);

      if (this.checkDuplicate(principal)) {
        this.send('gotError', get(this, 'intl').t('siteAccess.dupe'))
      } else {
        get(this, 'model.allowedPrincipalIds').pushObject(principal.id);
      }
    },

    removeAuthorized(id) {
      set(this, 'saved', false);

      get(this, 'model.allowedPrincipalIds').removeObject(id);
    },

    save(btnCb) {
      this.send('clearError');

      if ( get(this, 'model.accessMode') === 'required' && !get(this, 'model.allowedPrincipalIds.length') ) {
        this.send('gotError', get(this, 'intl').t('siteAccess.minimum'));

        btnCb();

        return;
      }

      set(this, 'saved', false);

      const model = get(this, 'model');

      model.save().then(() => {
        set(this, 'saved', true);
      }).catch((err) => {
        this.send('gotError', err);
      }).finally(() => {
        btnCb();
      });
    },

    gotError(err) {
      set(this, 'errors', [Errors.stringify(err)]);
    },

    clearError() {
      set(this, 'errors', null);

      setProperties(this, {
        errors:    null,
      });
    },
  },

  showList: computed('model.accessMode', function() {
    return get(this, 'model.accessMode') !== 'unrestricted';
  }),

  accessModeChanged: on('init', observer('model.accessMode', function() {
    set(this, 'saved',false);

    const allowedPrincipals = get(this, 'model.allowedPrincipalIds') || []; // ['princ_id1://yada']

    if ( get(this, 'model.accessMode') !== 'unrestricted' ) {

      const me    = get(this, 'access.principal')||[];
      const found = allowedPrincipals.filter(ident => get(me, 'id') === ident).length > 0;

      if ( !found ) {
        allowedPrincipals.pushObject(get(me, 'id'));
      }
    }

    set(this, 'model.allowedPrincipalIds', allowedPrincipals);
  })),

  checkDuplicate(principal) {
    return (get(this, 'model.allowedPrincipalIds')||[]).includes(principal.id) ? true : false;
  },

});
<commit_msg>Fix duplicate `me` users from being added to site access<commit_after>import { inject as service } from '@ember/service';
import Component from '@ember/component';
import Errors from 'ui/utils/errors';
import layout from './template';
import { get, set, observer, computed, setProperties } from '@ember/object';
import { on } from '@ember/object/evented';

export default Component.extend({
  layout,
  tagName:     'section',
  classNames:  ['well'],
  settings:    service(),
  access:      service(),
  github:      service(),
  intl:        service(),

  model:       null,
  individuals: 'siteAccess.users',
  collection:  'siteAccess.groups',
  principals:  null,

  saved:       true,
  errors:      null,

  actions: {
    addAuthorized(principal) {
      if ( !principal ) {
        return;
      }

      this.send('clearError');

      set(this, 'saved', false);

      if (this.checkDuplicate(principal)) {
        this.send('gotError', get(this, 'intl').t('siteAccess.dupe'))
      } else {
        get(this, 'model.allowedPrincipalIds').pushObject(principal.id);
      }
    },

    removeAuthorized(id) {
      set(this, 'saved', false);

      get(this, 'model.allowedPrincipalIds').removeObject(id);
    },

    save(btnCb) {
      this.send('clearError');

      if ( get(this, 'model.accessMode') === 'required' && !get(this, 'model.allowedPrincipalIds.length') ) {
        this.send('gotError', get(this, 'intl').t('siteAccess.minimum'));

        btnCb();

        return;
      }

      set(this, 'saved', false);

      const model = get(this, 'model');

      model.save().then(() => {
        set(this, 'saved', true);
      }).catch((err) => {
        this.send('gotError', err);
      }).finally(() => {
        btnCb();
      });
    },

    gotError(err) {
      set(this, 'errors', [Errors.stringify(err)]);
    },

    clearError() {
      set(this, 'errors', null);

      setProperties(this, {
        errors:    null,
      });
    },
  },

  showList: computed('model.accessMode', function() {
    return get(this, 'model.accessMode') !== 'unrestricted';
  }),

  accessModeChanged: on('init', observer('model.accessMode', function() {
    set(this, 'saved',false);

    const allowedPrincipals = get(this, 'model.allowedPrincipalIds') || []; // ['princ_id1://yada']

    if ( get(this, 'model.accessMode') !== 'unrestricted' ) {

      let found    = false;

      const myPIds = get(this, 'access.me.principalIds');

      myPIds.forEach( id => {
        if (allowedPrincipals.indexOf(id) >= 0) {
          found = true;
        }
      });

      if ( !found ) {
        allowedPrincipals.pushObject(get(this, 'access.me.id'));
      }
    }

    set(this, 'model.allowedPrincipalIds', allowedPrincipals);
  })),

  checkDuplicate(principal) {
    return (get(this, 'model.allowedPrincipalIds')||[]).includes(principal.id) ? true : false;
  },

});
<|endoftext|>


<commit_before>{
  rememberSelection: function(selModel, selectedRecords) {
    if (!this.rendered || Ext.isEmpty(this.el)) {
      return;
    }

    this.selectedRecords = this.getSelectionModel().getSelection();
  },
  refreshSelection: function() {
    if (0 >= this.selectedRecords.length) {
      return;
    }

    var newRecordsToSelect = [];
    for (var i = 0; i < this.selectedRecords.length; i++) {
      record = this.getStore().getById(this.selectedRecords[i].getId());
      if (!Ext.isEmpty(record)) {
        newRecordsToSelect.push(record);
      }
    }

    this.getSelectionModel().select(newRecordsToSelect);
  }
}

<commit_msg>Added safeguard for grids which don't autoload their store<commit_after>{
  rememberSelection: function(selModel, selectedRecords) {
    if (!this.rendered || Ext.isEmpty(this.el)) {
      return;
    }

    this.selectedRecords = this.getSelectionModel().getSelection();
  },
  refreshSelection: function() {
    if (!this.selectedRecords.length || 0 >= this.selectedRecords.length) {
      return;
    }

    var newRecordsToSelect = [];
    for (var i = 0; i < this.selectedRecords.length; i++) {
      record = this.getStore().getById(this.selectedRecords[i].getId());
      if (!Ext.isEmpty(record)) {
        newRecordsToSelect.push(record);
      }
    }

    this.getSelectionModel().select(newRecordsToSelect);
  }
}

<|endoftext|>


<commit_before>const fs = require('../../core/fs');
const shellJs = require('shelljs');
const utils = require('../../utils/utils');
const ProcessLauncher = require('../../process/processLauncher');
const constants = require('../../constants');
const StorageUtils = require('./storageUtils');

class StorageProcessesLauncher {
  constructor(options) {
    this.logger = options.logger;
    this.events = options.events;
    this.storageConfig = options.storageConfig;
    this.webServerConfig = options.webServerConfig;
    this.blockchainConfig = options.blockchainConfig;
    this.processes = {};

    this.cors = this.buildCors();

    this.events.on('exit', () => {
      Object.keys(this.processes).forEach(processName => {
        this.processes[processName].send('exit');
      });
    });
  }

  buildCors(storageName)
  {
    let corsParts = [];
    // add our webserver CORS
    if(this.webServerConfig.enabled){
      if (this.webServerConfig && this.webServerConfig.host) {
        corsParts.push(utils.buildUrlFromConfig(this.webServerConfig));
      }
      else corsParts.push('http://localhost:8000');
    }

    // add all dapp connection storage
    if(this.storageConfig.enabled) {
      this.storageConfig.dappConnection.forEach(dappConn => {
        if(dappConn.provider === storageName) return; // do not add CORS URL for ourselves
        if(dappConn.getUrl || dappConn.host){

          // if getUrl is specified in the config, that needs to be included in cors
          // instead of the concatenated protocol://host:port
          if(dappConn.getUrl) {
            // remove /ipfs or /bzz: from getUrl if it's there
            let getUrlParts = dappConn.getUrl.split('/');
            getUrlParts = getUrlParts.slice(0, 3);
            corsParts.push(getUrlParts.join('/'));
          }
          // in case getUrl wasn't specified, use a built url
          else{
            corsParts.push(utils.buildUrlFromConfig(dappConn));
          }
        }
      });
    }

    if(this.blockchainConfig.enabled) {
      // add our rpc endpoints to CORS
      if(this.blockchainConfig.rpcHost && this.blockchainConfig.rpcPort){
        corsParts.push(`http://${this.blockchainConfig.rpcHost}:${this.blockchainConfig.rpcPort}`);
      }

      // add our ws endpoints to CORS
      if(this.blockchainConfig.wsRPC && this.blockchainConfig.wsHost && this.blockchainConfig.wsPort){
        corsParts.push(`ws://${this.blockchainConfig.wsHost}:${this.blockchainConfig.wsPort}`);
      }
    }
    return corsParts;
  }

  processExited(storageName, code) {
    this.logger.error(__(`Storage process for {{storageName}} ended before the end of this process. Code: {{code}}`, {storageName, code}));
  }

  launchProcess(storageName, callback) {
    const self = this;
    if (self.processes[storageName]) {
      return callback(__('Storage process already started'));
    }
    const filePath = utils.joinPath(__dirname, `./${storageName}.js`);
    fs.access(filePath, (err) => {
      if (err) {
        return callback(__('No process file for this storage type (%s) exists. Please start the process locally.', storageName));
      }

      const program = shellJs.which(StorageUtils.getCommand(storageName, self.storageConfig));
      if (!program) {
        self.logger.warn(__('{{storageName}} is not installed or your configuration is not right', {storageName}).yellow);
        self.logger.info(__('You can install and get more information here: ').yellow + StorageUtils.getStorageInstallationSite(storageName).underline);
        return callback(__('%s not installed', storageName));
      }

      self.logger.info(__(`Starting %s process`, storageName).cyan);
      self.processes[storageName] = new ProcessLauncher({
        modulePath: filePath,
        logger: self.logger,
        events: self.events,
        silent: self.logger.logLevel !== 'trace',
        exitCallback: self.processExited.bind(this, storageName)
      });
      self.processes[storageName].send({
        action: constants.blockchain.init, options: {
          storageConfig: self.storageConfig,
          cors: self.buildCors(storageName)
        }
      });

      self.processes[storageName].on('result', constants.storage.initiated, (msg) => {
        if (msg.error) {
          self.processes[storageName].disconnect();
          delete self.processes[storageName];
          return callback(msg.error);
        }
        self.logger.info(__(`${storageName} process started`).cyan);
        callback();
      });
    });
  }
}

module.exports = StorageProcessesLauncher;
<commit_msg>storage procs launcher -- canonicalHost<commit_after>const fs = require('../../core/fs');
const shellJs = require('shelljs');
const utils = require('../../utils/utils');
const ProcessLauncher = require('../../process/processLauncher');
const constants = require('../../constants');
const StorageUtils = require('./storageUtils');
const {canonicalHost} = require('../../utils/host');

class StorageProcessesLauncher {
  constructor(options) {
    this.logger = options.logger;
    this.events = options.events;
    this.storageConfig = options.storageConfig;
    this.webServerConfig = options.webServerConfig;
    this.blockchainConfig = options.blockchainConfig;
    this.processes = {};

    this.cors = this.buildCors();

    this.events.on('exit', () => {
      Object.keys(this.processes).forEach(processName => {
        this.processes[processName].send('exit');
      });
    });
  }

  buildCors(storageName)
  {
    let corsParts = [];
    // add our webserver CORS
    if(this.webServerConfig.enabled){
      if (this.webServerConfig && this.webServerConfig.host) {
        corsParts.push(utils.buildUrlFromConfig(this.webServerConfig));
      }
      else corsParts.push('http://localhost:8000');
    }

    // add all dapp connection storage
    if(this.storageConfig.enabled) {
      this.storageConfig.dappConnection.forEach(dappConn => {
        if(dappConn.provider === storageName) return; // do not add CORS URL for ourselves
        if(dappConn.getUrl || dappConn.host){

          // if getUrl is specified in the config, that needs to be included in cors
          // instead of the concatenated protocol://host:port
          if(dappConn.getUrl) {
            // remove /ipfs or /bzz: from getUrl if it's there
            let getUrlParts = dappConn.getUrl.split('/');
            getUrlParts = getUrlParts.slice(0, 3);
            let host = canonicalHost(getUrlParts[2].split(':')[0]);
            let port = getUrlParts[2].split(':')[1];
            getUrlParts[2] = port ? [host, port].join(':') : host;
            corsParts.push(getUrlParts.join('/'));
          }
          // in case getUrl wasn't specified, use a built url
          else{
            corsParts.push(utils.buildUrlFromConfig(dappConn));
          }
        }
      });
    }

    if(this.blockchainConfig.enabled) {
      // add our rpc endpoints to CORS
      if(this.blockchainConfig.rpcHost && this.blockchainConfig.rpcPort){
        corsParts.push(`http://${canonicalHost(this.blockchainConfig.rpcHost)}:${this.blockchainConfig.rpcPort}`);
      }

      // add our ws endpoints to CORS
      if(this.blockchainConfig.wsRPC && this.blockchainConfig.wsHost && this.blockchainConfig.wsPort){
        corsParts.push(`ws://${canonicalHost(this.blockchainConfig.wsHost)}:${this.blockchainConfig.wsPort}`);
      }
    }
    return corsParts;
  }

  processExited(storageName, code) {
    this.logger.error(__(`Storage process for {{storageName}} ended before the end of this process. Code: {{code}}`, {storageName, code}));
  }

  launchProcess(storageName, callback) {
    const self = this;
    if (self.processes[storageName]) {
      return callback(__('Storage process already started'));
    }
    const filePath = utils.joinPath(__dirname, `./${storageName}.js`);
    fs.access(filePath, (err) => {
      if (err) {
        return callback(__('No process file for this storage type (%s) exists. Please start the process locally.', storageName));
      }

      const program = shellJs.which(StorageUtils.getCommand(storageName, self.storageConfig));
      if (!program) {
        self.logger.warn(__('{{storageName}} is not installed or your configuration is not right', {storageName}).yellow);
        self.logger.info(__('You can install and get more information here: ').yellow + StorageUtils.getStorageInstallationSite(storageName).underline);
        return callback(__('%s not installed', storageName));
      }

      self.logger.info(__(`Starting %s process`, storageName).cyan);
      self.processes[storageName] = new ProcessLauncher({
        modulePath: filePath,
        logger: self.logger,
        events: self.events,
        silent: self.logger.logLevel !== 'trace',
        exitCallback: self.processExited.bind(this, storageName)
      });
      self.processes[storageName].send({
        action: constants.blockchain.init, options: {
          storageConfig: self.storageConfig,
          cors: self.buildCors(storageName)
        }
      });

      self.processes[storageName].on('result', constants.storage.initiated, (msg) => {
        if (msg.error) {
          self.processes[storageName].disconnect();
          delete self.processes[storageName];
          return callback(msg.error);
        }
        self.logger.info(__(`${storageName} process started`).cyan);
        callback();
      });
    });
  }
}

module.exports = StorageProcessesLauncher;
<|endoftext|>