<commit_before>'use strict';

var request = require('request');
var should = require('chai').should();  // actually call the function
var config = require('../config');
var subscriptions = require('../orion/subscriptions');

var srv = config.getSubService('lighting_simulations');
var headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Fiware-Service': srv.service,
  'Fiware-ServicePath': srv.subservice
};

describe('ORION', function(){

  it('Dummy headers check', function(done){
    headers['Content-Type'].should.be.equal('application/json');
    headers['Fiware-Service'].should.be.equal('urbo');
    headers['Fiware-ServicePath'].should.be.equal('/geographica_dev');
    done();
  });

  it('Initialize subscriptions', function(done){
    this.timeout(0);
    subscriptions.initialize(function(error, subs){
      if(error instanceof Array) {
        should.equal(error[0], null);
      }
      else {
        should.equal(error, null);
      }

      done();
    });
  });

});<commit_msg>Test request<commit_after>'use strict';

var request = require('request');
var should = require('chai').should();  // actually call the function
var config = require('../config');
var subscriptions = require('../orion/subscriptions');

var srv = config.getSubService('lighting_simulations');
var headers = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
  'Fiware-Service': srv.service,
  'Fiware-ServicePath': srv.subservice
};

var requestBody = {
    url: 'http://localhost:3000/subscriptions/lighting_stcabinet_state'
    method: 'POST',
    type: 'connectorPsql',
    data: {
       title: 'lighting_stcabinet_state to PSQL',
       "contextResponses":[
          {
             "contextElement":{
                "type":"ht_6",
                "isPattern":"false",
                "id":"sepulveda-01",
                "attributes":[
                   {
                      "name":"energyConsumed",
                      "type":"Number",
                      "value":"0",
                      "metadatas":[
                         {
                            "name":"TimeInstant",
                            "type":"DateTime",
                            "value":"2017-01-17T11:30:30.828Z"
                         },
                         {
                            "name":"dateUpdated",
                            "type":"DateTime",
                            "value":"2017-01-17T11:30:30.828Z"
                         },
                         {
                            "name":"uom",
                            "type":"Text",
                            "value":"kVArh"
                         }
                      ]
                   },
                   {
                      "name":"reactiveEnergyConsumed",
                      "type":"Number",
                      "value":"0",
                      "metadatas":[
                         {
                            "name":"TimeInstant",
                            "type":"DateTime",
                            "value":"2017-01-17T11:30:30.837Z"
                         },
                         {
                            "name":"dateUpdated",
                            "type":"DateTime",
                            "value":"2017-01-17T11:30:30.837Z"
                         },
                         {
                            "name":"uom",
                            "type":"Text",
                            "value":"kVArh"
                         }
                      ]
                   },
                   {
                      "name":"timeInstant",
                      "type":"DateTime",
                      "value":"2017-01-17T11:30:30.876Z"
                   },
                   {
                      "name":"totalActivePower",
                      "type":"Number",
                      "value":"0",
                      "metadatas":[
                         {
                            "name":"TimeInstant",
                            "type":"DateTime",
                            "value":"2017-01-17T11:30:30.818Z"
                         },
                         {
                            "name":"dateUpdated",
                            "type":"DateTime",
                            "value":"2017-01-17T11:30:30.818Z"
                         }
                      ]
                   }
                ]
             },
             "statusCode":{
                "code":"200",
                "reasonPhrase":"OK"
             }
          }
       ],
       "subscription":{
          "id":"lighting_stcabinet_state",
          "subservice_id":"lighting_simulations",
          "schemaname":"distrito_telefonica",
          "subsduration":"P8M",
          "substhrottling":"PT0S",
          "fetchDataOnCreated":false,
          "entityTypes":[
             {
                "typeName":"ht_6"
             }
          ],
          "mode":"append",
          "attributes":[
             {
                "name":"timeInstant",
                "namedb":"TimeInstant",
                "type":"ISO8601",
                "indexdb":true,
                "cartodb":true,
                "constraint":true
             },
             {
                "name":"energyConsumed",
                "namedb":"energyconsumed",
                "type":"float",
                "cartodb":true
             },
             {
                "name":"reactiveEnergyConsumed",
                "namedb":"reactiveenergyconsumed",
                "type":"float",
                "cartodb":true
             },
             {
                "name":"totalActivePower",
                "namedb":"totalactivepower",
                "type":"float",
                "cartodb":true
             }
          ],
          "trigger_attributes":[
             "totalActivePower"
          ]
       }
    },
    options:{
       attempts: 3,
       priority: 'critical'
    }
 }

describe('ORION', function(){

  it('Dummy headers check', function(done){
    headers['Content-Type'].should.be.equal('application/json');
    headers['Fiware-Service'].should.be.equal('urbo');
    headers['Fiware-ServicePath'].should.be.equal('/geographica_dev');
    done();
  });

  it('Initialize subscriptions', function(done){
    this.timeout(0);
    subscriptions.initialize(function(error, subs){
      if(error instanceof Array) {
        should.equal(error[0], null);
      }
      else {
        should.equal(error, null);
      }

      request(requestBody, function(error, response, body){
        console.log(error);
        console.log(body);
        console.log(response);
        done();

      })

    });
  });

});<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const Router = require('./index');
const middleware = require('storj-service-middleware');
const rawbody = middleware.rawbody;
const log = require('../../logger');
const errors = require('storj-service-error-types');
const merge = require('merge');
const inherits = require('util').inherits;
const authenticate = middleware.authenticate;
const crypto = require('crypto');
const storj = require('storj-lib');
const analytics = require('storj-analytics');
const defaults = require('../limiter').DEFAULTS;

/**
 * Handles endpoints for all user related operations
 * @constructor
 * @extends {Router}
 */
function UsersRouter(options) {
  if (!(this instanceof UsersRouter)) {
    return new UsersRouter(options);
  }

  Router.apply(this, arguments);

  this._verify = authenticate(this.storage);
  this._limiter = middleware.rateLimiter(options.redis);
}

inherits(UsersRouter, Router);

/**
 * Sends a user activation email
 * @private
 */
UsersRouter.prototype._dispatchActivationEmail = function(user, redir, cb) {
  let self = this;
  let profile = self.config.server.public || self.config.server;
  let host = profile.host;
  let callback = cb || storj.utils.noop;
  let port = [443, 80].indexOf(profile.port) === -1 ?
             ':' + profile.port :
             '';
  let proto = self.config.server.ssl &&
              self.config.server.ssl.cert &&
              self.config.server.ssl.key ?
              'https:' :
              'http:';

  self.mailer.dispatch(user.email, 'confirm', {
    token: user.activator,
    redirect: redir,
    url: proto + '//' + host + port
  }, function(err) {
    if (err) {
      log.error('failed to send activation email, reason: %s', err.message);
      callback(err);
    } else {
      callback(null);
    }
  });
};

/**
 * Registers a new user
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.createUser = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;
  const PublicKey = this.storage.models.PublicKey;

  log.info('registering user account for %s', req.body.email);

  User.create(req.body.email, req.body.password, function(err, user) {
    if (err) {
      return next(err);
    }

    analytics.identify(req.headers.dnt, {
      userId: user.uuid,
      traits: {
        activated: false
      }
    });
    analytics.track(req.headers.dnt, {
      userId: user.uuid,
      event: 'User Created'
    });

    if (!req.body.pubkey) {
      self._dispatchActivationEmail(user, req.body.redirect);
      return res.status(201).send(user.toObject());
    }

    PublicKey.create(user, req.body.pubkey, function(err, pubkey) {
      if (err) {
        user.remove();
        return next(new errors.BadRequestError(err.message));
      }

      self._dispatchActivationEmail(user, req.body.redirect);
      res.status(201).send(merge(user.toObject(), {
        pubkey: pubkey.key
      }));
    });
  });
};

/**
 * Confirms a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.confirmActivateUser = function(req, res, next) {
  const User = this.storage.models.User;

  log.info('activating user with token %s', req.params.token);

  User.findOne({
    activator: req.params.token
  }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.BadRequestError('Invalid activation token'));
    }

    user.activate(function(err) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      analytics.track(req.headers.dnt, {
        userId: user.uuid,
        event: 'User Activated'
      });
      analytics.identify(req.headers.dnt, {
        userId: user.uuid,
        traits: {
          activated: true
        }
      });

      if (req.query.redirect) {
        res.redirect(req.query.redirect);
      } else {
        res.send(user.toObject());
      }
    });
  });
};

/**
 * Adds partner referral id
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.addPartnerReferral = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;
  const Partner = this.storage.models.Partner;

  log.info('adding partner referral %s', req.body.partner);

  if (!req.body.partner) {
    return next(new errors.BadRequestError('Missing partner data'));
  }

  Partner.findOne({ name: req.body.partner }, function(err, partner) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }
    if (!partner) {
      return next(new errors.NotFoundError('Partner not found'));
    }

    User.findOne({ _id: req.params.id }, function(err, user) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }
      if (!user) {
        return next(new errors.NotFoundError('User not found'));
      }
      // Prevents changing initial referrer (?)
      if (user.partnerReferral) {
        return next(new errors.BadRequestError('Partner already exists'));
      }

      user.partnerReferral = partner.id;

      user.save(function(err) {
        if (err) {
          return next(new errors.InternalError(err.message));
        }

        analytics.identify(req.headers.dnt, {
          userId: user.uuid,
          traits: {
            partnerReferral: partner.id
          }
        });

        analytics.track(req.headers.dnt, {
          userId: user.uuid,
          event: 'Partner Referral Added'
        });

        res.status(200).send(user.toObject());
      });
    });
  });
};

/**
 * Reactivates a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.reactivateUser = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;

  log.info('sending account reactivation email to %s', req.body.email);

  User.findOne({
    _id: req.body.email
  }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    if (user.activated) {
      return next(new errors.BadRequestError('User is already activated'));
    }

    self._dispatchActivationEmail(user, req.body.redirect);
    res.status(201).send(user.toObject());
  });
};

/**
 * Deactivates a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.destroyUser = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;

  log.info('creating user deactivation token');

  User.findOne({ _id: req.params.id }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    if (req.user._id !== user._id) {
      return next(new errors.NotAuthorizedError());
    }

    user.deactivator = crypto.randomBytes(256).toString('hex');

    let profile = self.config.server.public || self.config.server;
    let host = profile.host;
    let port = [443, 80].indexOf(profile.port) === -1 ?
               ':' + profile.port :
               '';
    let proto = self.config.server.ssl &&
                self.config.server.ssl.cert &&
                self.config.server.ssl.key ?
                'https:' :
                'http:';

    self.mailer.dispatch(user.email, 'delete', {
      token: user.deactivator,
      redirect: req.body.redirect,
      url: proto + '//' + host + port
    }, function(err) {
      if (err) {
        log.error('failed to send deactivation email, reason: %s', err.message);
        return next(new errors.InternalError(err.message));
      }

      user.save(function(err) {
        if (err) {
          return next(new errors.InternalError(err.message));
        }

        res.status(200).send(user.toObject());
      });
    });
  });
};

/**
 * Confirms the deletion of a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.confirmDestroyUser = function(req, res, next) {
  const User = this.storage.models.User;

  log.info('deactivating user account with token %s', req.params.token);

  User.findOne({ deactivator: req.params.token }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    user.deactivate(function(err) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      if (req.query.redirect) {
        res.redirect(req.query.redirect);
      } else {
        res.status(200).send(user.toObject());
      }
    });
  });
};

/**
 * Creates a password reset token
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.createPasswordResetToken = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;

  try {
    assert(Buffer(req.body.password, 'hex').length * 8 === 256);
  } catch (err) {
    return next(new errors.BadRequestError(
      'Password must be hex encoded SHA-256 hash'
    ));
  }

  User.findOne({ _id: req.params.id }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    log.info('creating password reset token for %s', user._id);

    user.resetter = crypto.randomBytes(256).toString('hex');
    user.pendingHashPass = crypto.createHash('sha256').update(req.body.password).digest('hex');

    user.save(function(err) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      let profile = self.config.server.public || self.config.server;
      let host = profile.host;
      let port = [443, 80].indexOf(profile.port) === -1 ?
                 ':' + profile.port :
                 '';
      let proto = self.config.server.ssl &&
                  self.config.server.ssl.cert &&
                  self.config.server.ssl.key ?
                  'https:' :
                  'http:';

      self.mailer.dispatch(user.email, 'reset', {
        token: user.resetter,
        redirect: req.body.redirect,
        url: proto + '//' + host + port
      }, function(err) {
        if (err) {
          log.error('failed to send reset email, reason: %s', err.message);
          return next(new errors.InternalError(err.message));
        }

        res.status(200).send(user.toObject());
      });
    });
  });
};

/**
 * Confirms and applies the password reset
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.confirmPasswordReset = function(req, res, next) {
  const User = this.storage.models.User;

  // NB: Mitigate timing attack
  // NB: - attempt to make non-lookup-responses take similar
  // NB: - amounts of time as lookup-responses
  User.count({}, function(err, count) {
    let rand = Math.floor(Math.random() * count);

    function nextPlusTime(value) {
      User.findOne({}).skip(rand).exec(function() { // do nothing with this record
        next(value);
      });
    }

    try {
      assert(Buffer(req.params.token, 'hex').length === 256);
    } catch (err) {
      return nextPlusTime(new Error('Resetter must be hex encoded 256 byte string'));
    }

    User.findOne({ resetter: req.params.token }, function(err, user) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      if (!user) {
        return next(new errors.NotFoundError('User not found'));
      }

      user.hashpass = user.pendingHashPass;
      user.pendingHashPass = null;
      user.resetter = null;

      user.save(function(err) {
        if (err) {
          return next(new errors.InternalError(err.message));
        }

        if (req.query.redirect) {
          res.redirect(req.query.redirect);
        } else {
          res.send(user.toObject());
        }
      });
    });
  });
};

/**
 * Export definitions
 * @private
 */
UsersRouter.prototype._definitions = function() {
  return [
    ['POST', '/users', this._limiter(defaults), rawbody, this.createUser],
    ['POST', '/activations', this._limiter(defaults), rawbody, this.reactivateUser],
    ['GET', '/activations/:token', this._limiter(defaults), this.confirmActivateUser],
    ['PUT', '/users/:id/partner', this._limiter(defaults), this._verify, rawbody, this.addPartnerReferral],
    ['DELETE', '/users/:id', this._limiter(defaults), this._verify, this.destroyUser],
    ['GET', '/deactivations/:token', this._limiter(defaults), this.confirmDestroyUser],
    ['PATCH', '/users/:id', this._limiter(defaults), rawbody, this.createPasswordResetToken],
    ['GET', '/resets/:token', this._limiter(defaults), this.confirmPasswordReset]
  ];
};

module.exports = UsersRouter;
<commit_msg>Update variable names<commit_after>'use strict';

const assert = require('assert');
const Router = require('./index');
const middleware = require('storj-service-middleware');
const rawbody = middleware.rawbody;
const log = require('../../logger');
const errors = require('storj-service-error-types');
const merge = require('merge');
const inherits = require('util').inherits;
const authenticate = middleware.authenticate;
const crypto = require('crypto');
const storj = require('storj-lib');
const analytics = require('storj-analytics');
const defaults = require('../limiter').DEFAULTS;

/**
 * Handles endpoints for all user related operations
 * @constructor
 * @extends {Router}
 */
function UsersRouter(options) {
  if (!(this instanceof UsersRouter)) {
    return new UsersRouter(options);
  }

  Router.apply(this, arguments);

  this._verify = authenticate(this.storage);
  this._limiter = middleware.rateLimiter(options.redis);
}

inherits(UsersRouter, Router);

/**
 * Sends a user activation email
 * @private
 */
UsersRouter.prototype._dispatchActivationEmail = function(user, redir, cb) {
  let self = this;
  let profile = self.config.server.public || self.config.server;
  let host = profile.host;
  let callback = cb || storj.utils.noop;
  let port = [443, 80].indexOf(profile.port) === -1 ?
             ':' + profile.port :
             '';
  let proto = self.config.server.ssl &&
              self.config.server.ssl.cert &&
              self.config.server.ssl.key ?
              'https:' :
              'http:';

  self.mailer.dispatch(user.email, 'confirm', {
    token: user.activator,
    redirect: redir,
    url: proto + '//' + host + port
  }, function(err) {
    if (err) {
      log.error('failed to send activation email, reason: %s', err.message);
      callback(err);
    } else {
      callback(null);
    }
  });
};

/**
 * Registers a new user
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.createUser = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;
  const PublicKey = this.storage.models.PublicKey;

  log.info('registering user account for %s', req.body.email);

  User.create(req.body.email, req.body.password, function(err, user) {
    if (err) {
      return next(err);
    }

    analytics.identify(req.headers.dnt, {
      userId: user.uuid,
      traits: {
        activated: false
      }
    });
    analytics.track(req.headers.dnt, {
      userId: user.uuid,
      event: 'User Created'
    });

    if (!req.body.pubkey) {
      self._dispatchActivationEmail(user, req.body.redirect);
      return res.status(201).send(user.toObject());
    }

    PublicKey.create(user, req.body.pubkey, function(err, pubkey) {
      if (err) {
        user.remove();
        return next(new errors.BadRequestError(err.message));
      }

      self._dispatchActivationEmail(user, req.body.redirect);
      res.status(201).send(merge(user.toObject(), {
        pubkey: pubkey.key
      }));
    });
  });
};

/**
 * Confirms a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.confirmActivateUser = function(req, res, next) {
  const User = this.storage.models.User;

  log.info('activating user with token %s', req.params.token);

  User.findOne({
    activator: req.params.token
  }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.BadRequestError('Invalid activation token'));
    }

    user.activate(function(err) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      analytics.track(req.headers.dnt, {
        userId: user.uuid,
        event: 'User Activated'
      });
      analytics.identify(req.headers.dnt, {
        userId: user.uuid,
        traits: {
          activated: true
        }
      });

      if (req.query.redirect) {
        res.redirect(req.query.redirect);
      } else {
        res.send(user.toObject());
      }
    });
  });
};

/**
 * Adds partner referral id
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.addReferralPartner = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;
  const Partner = this.storage.models.Partner;

  log.info('adding partner referral %s', req.body.partner);

  if (!req.body.partner) {
    return next(new errors.BadRequestError('Missing partner data'));
  }

  Partner.findOne({ name: req.body.partner }, function(err, partner) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }
    if (!partner) {
      return next(new errors.NotFoundError('Partner not found'));
    }

    User.findOne({ _id: req.params.id }, function(err, user) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }
      if (!user) {
        return next(new errors.NotFoundError('User not found'));
      }
      // Prevents changing initial referrer (?)
      if (user.referralPartner) {
        return next(new errors.BadRequestError('Partner already exists'));
      }

      user.referralPartner = partner.id;

      user.save(function(err) {
        if (err) {
          return next(new errors.InternalError(err.message));
        }

        analytics.identify(req.headers.dnt, {
          userId: user.uuid,
          traits: {
            referralPartner: partner.id
          }
        });

        analytics.track(req.headers.dnt, {
          userId: user.uuid,
          event: 'Referral Partner Added'
        });

        res.status(200).send(user.toObject());
      });
    });
  });
};

/**
 * Reactivates a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.reactivateUser = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;

  log.info('sending account reactivation email to %s', req.body.email);

  User.findOne({
    _id: req.body.email
  }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    if (user.activated) {
      return next(new errors.BadRequestError('User is already activated'));
    }

    self._dispatchActivationEmail(user, req.body.redirect);
    res.status(201).send(user.toObject());
  });
};

/**
 * Deactivates a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.destroyUser = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;

  log.info('creating user deactivation token');

  User.findOne({ _id: req.params.id }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    if (req.user._id !== user._id) {
      return next(new errors.NotAuthorizedError());
    }

    user.deactivator = crypto.randomBytes(256).toString('hex');

    let profile = self.config.server.public || self.config.server;
    let host = profile.host;
    let port = [443, 80].indexOf(profile.port) === -1 ?
               ':' + profile.port :
               '';
    let proto = self.config.server.ssl &&
                self.config.server.ssl.cert &&
                self.config.server.ssl.key ?
                'https:' :
                'http:';

    self.mailer.dispatch(user.email, 'delete', {
      token: user.deactivator,
      redirect: req.body.redirect,
      url: proto + '//' + host + port
    }, function(err) {
      if (err) {
        log.error('failed to send deactivation email, reason: %s', err.message);
        return next(new errors.InternalError(err.message));
      }

      user.save(function(err) {
        if (err) {
          return next(new errors.InternalError(err.message));
        }

        res.status(200).send(user.toObject());
      });
    });
  });
};

/**
 * Confirms the deletion of a user account
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.confirmDestroyUser = function(req, res, next) {
  const User = this.storage.models.User;

  log.info('deactivating user account with token %s', req.params.token);

  User.findOne({ deactivator: req.params.token }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    user.deactivate(function(err) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      if (req.query.redirect) {
        res.redirect(req.query.redirect);
      } else {
        res.status(200).send(user.toObject());
      }
    });
  });
};

/**
 * Creates a password reset token
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.createPasswordResetToken = function(req, res, next) {
  const self = this;
  const User = this.storage.models.User;

  try {
    assert(Buffer(req.body.password, 'hex').length * 8 === 256);
  } catch (err) {
    return next(new errors.BadRequestError(
      'Password must be hex encoded SHA-256 hash'
    ));
  }

  User.findOne({ _id: req.params.id }, function(err, user) {
    if (err) {
      return next(new errors.InternalError(err.message));
    }

    if (!user) {
      return next(new errors.NotFoundError('User not found'));
    }

    log.info('creating password reset token for %s', user._id);

    user.resetter = crypto.randomBytes(256).toString('hex');
    user.pendingHashPass = crypto.createHash('sha256').update(req.body.password).digest('hex');

    user.save(function(err) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      let profile = self.config.server.public || self.config.server;
      let host = profile.host;
      let port = [443, 80].indexOf(profile.port) === -1 ?
                 ':' + profile.port :
                 '';
      let proto = self.config.server.ssl &&
                  self.config.server.ssl.cert &&
                  self.config.server.ssl.key ?
                  'https:' :
                  'http:';

      self.mailer.dispatch(user.email, 'reset', {
        token: user.resetter,
        redirect: req.body.redirect,
        url: proto + '//' + host + port
      }, function(err) {
        if (err) {
          log.error('failed to send reset email, reason: %s', err.message);
          return next(new errors.InternalError(err.message));
        }

        res.status(200).send(user.toObject());
      });
    });
  });
};

/**
 * Confirms and applies the password reset
 * @param {http.IncomingMessage} req
 * @param {http.ServerResponse} res
 * @param {Function} next
 */
UsersRouter.prototype.confirmPasswordReset = function(req, res, next) {
  const User = this.storage.models.User;

  // NB: Mitigate timing attack
  // NB: - attempt to make non-lookup-responses take similar
  // NB: - amounts of time as lookup-responses
  User.count({}, function(err, count) {
    let rand = Math.floor(Math.random() * count);

    function nextPlusTime(value) {
      User.findOne({}).skip(rand).exec(function() { // do nothing with this record
        next(value);
      });
    }

    try {
      assert(Buffer(req.params.token, 'hex').length === 256);
    } catch (err) {
      return nextPlusTime(new Error('Resetter must be hex encoded 256 byte string'));
    }

    User.findOne({ resetter: req.params.token }, function(err, user) {
      if (err) {
        return next(new errors.InternalError(err.message));
      }

      if (!user) {
        return next(new errors.NotFoundError('User not found'));
      }

      user.hashpass = user.pendingHashPass;
      user.pendingHashPass = null;
      user.resetter = null;

      user.save(function(err) {
        if (err) {
          return next(new errors.InternalError(err.message));
        }

        if (req.query.redirect) {
          res.redirect(req.query.redirect);
        } else {
          res.send(user.toObject());
        }
      });
    });
  });
};

/**
 * Export definitions
 * @private
 */
UsersRouter.prototype._definitions = function() {
  return [
    ['POST', '/users', this._limiter(defaults), rawbody, this.createUser],
    ['POST', '/activations', this._limiter(defaults), rawbody, this.reactivateUser],
    ['GET', '/activations/:token', this._limiter(defaults), this.confirmActivateUser],
    ['PUT', '/users/:id/partner', this._limiter(defaults), this._verify, rawbody, this.addReferralPartner],
    ['DELETE', '/users/:id', this._limiter(defaults), this._verify, this.destroyUser],
    ['GET', '/deactivations/:token', this._limiter(defaults), this.confirmDestroyUser],
    ['PATCH', '/users/:id', this._limiter(defaults), rawbody, this.createPasswordResetToken],
    ['GET', '/resets/:token', this._limiter(defaults), this.confirmPasswordReset]
  ];
};

module.exports = UsersRouter;
<|endoftext|>


<commit_before>var Domain    = require('domain');
var async     = require('async');
var ripple    = require('ripple-lib');
var serverLib = require('../lib/server-lib');

exports.get = getTrustLines;

function getTrustLines($, req, res, next) {
  var remote = $.remote;

  var opts = req.params;

  Object.keys(req.query).forEach(function(param) {
    opts[param] = req.query[param];
  });

  opts.limit = opts.limit || req.body.limit;

  var currencyRE = new RegExp(opts.currency ? ('^' + opts.currency.toUpperCase() + '$') : /./);

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: account' });
    }

    if (opts.counterparty && !ripple.UInt160.is_valid(opts.counterparty)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: issuer' });
    }

    if (opts.currency && !/^[A-Z0-9]{3}$/.test(opts.currency)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid currency: currency' });
    }

    callback();
  };

 function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function getAccountLines(connected, callback) {
    if (!connected) {
      return res.json(500, { success: false, message: 'Remote is not connected' });
    }

    var request = remote.requestAccountLines(opts.account);

    if (opts.counterparty) {
      request.message.peer = opts.counterparty;
    }

    request.once('error', callback);

    request.once('success', function(res) {
      var lines = [ ];

      res.lines.forEach(function(line) {
        if (!currencyRE.test(line.currency)) return;
        lines.push({
          account: opts.account,
          counterparty: line.account,
          currency: line.currency,
          trust_limit: line.limit,
          reciprocated_trust_limit: line.limit_peer,
          account_allows_rippling: line.no_ripple ? !line.no_ripple : true,
          counterparty_allows_rippling: line.no_ripple_peer ? !line.no_ripple_peer : true,
        });
      });

      callback(null, lines);
    });

    request.request();
  };

  var steps = [
    validateOptions,
    ensureConnected,
    getAccountLines
  ]

  async.waterfall(steps, function(err, lines) {
    if (err) {
      next(err);
    } else {
      res.json(200, { success: true, lines: lines });
    }
  });
};

exports.add = addTrustLine;

function addTrustLine($, req, res, next) {
  var self = this;
  var remote = $.remote;
  var opts = req.params;

  Object.keys(req.body).forEach(function(param) {
    opts[param] = req.body[param];
  });

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a Ripple address: account' });
    }

    if (typeof opts.secret !== 'string') {
      return res.json(400, { success: false, message: 'Parameter is not a Ripple secret: secret' });
    }

    if (typeof opts.limit === 'string') {
      var spl = opts.limit.split('/');
      opts.limit = {
        amount:        spl[0],
        currency:      spl[1],
        counterparty:  spl[2]
      }
    }

    if (typeof opts.limit !== 'object') {
      return res.json(400, { success: false, message: 'Parameter is not a valid limit: limit' });
    }

    if (isNaN(opts.limit.amount)) {
      return res.json(400, { success: false, message: 'Parameter is not a number: limit.amount' });
    }

    if (!/^[A-Z0-9]{3}$/.test(opts.limit.currency)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid currency: limit.currency' });
    }

    if (!ripple.UInt160.is_valid(opts.limit.counterparty)) {
      return res.json(400, { success: false, message: 'Parameter is not a Ripple address: limit.counterparty' });
    }

    if (!/^(undefined|number)$/.test(typeof opts.quality_in)) {
      return res.json(400, { success: false, message: 'Parameter must be a number: quality_in' });
    }

    if (!/^(undefined|number)$/.test(typeof opts.quality_out)) {
      return res.json(400, { success: false, message: 'Parameter must be a number: quality_out' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function addLine(connected, callback) {
    if (!connected) {
      return res.json(500, { success: false, message: 'Remote is not connected' });
    }

    var domain = Domain.create();

    domain.once('error', callback);

    domain.run(function() {
      var limit = [ opts.limit.amount, opts.limit.currency, opts.limit.counterparty ].join('/');
      var transaction = remote.transaction().trustSet(opts.account, limit);

      transaction.secret(opts.secret);

      if (typeof opts.quality_in === 'number') {
        transaction.tx_json.QualityIn = opts.quality_in;
      }

      if (typeof opts.quality_out === 'number') {
        transaction.tx_json.QualityOut = opts.quality_out;
      }

      transaction.once('error', callback);

      transaction.once('proposed', function(m) {
        var summary = transaction.summary();
        callback(null, {
          account: opts.account,
          counterparty: opts.limit.counterparty,
          currency: opts.limit.currency,
          trust_limit: opts.limit.amount,
          ledger: String(summary.submitIndex)
        });
      });

      transaction.submit();
    });
  };

  var steps = [
    validateOptions,
    ensureConnected,
    addLine
  ]

  async.waterfall(steps, function(err, line) {
    if (err) {
      next(err);
    } else {
      res.json(200, { success: true, line: line });
    }
  });
};
<commit_msg>Add no_rippling info<commit_after>var Domain    = require('domain');
var async     = require('async');
var ripple    = require('ripple-lib');
var serverLib = require('../lib/server-lib');

const TrustSetFlags = {
  SetAuth:        { name: 'authorized', value: 0x00010000 },
  NoRipple:       { name: 'prevent_rippling', value: 0x00020000 },
  ClearNoRipple:  { name: 'allow_rippling', value: 0x00040000 }
}

exports.get = getTrustLines;

function getTrustLines($, req, res, next) {
  var remote = $.remote;

  var opts = req.params;

  Object.keys(req.query).forEach(function(param) {
    opts[param] = req.query[param];
  });

  opts.limit = opts.limit || req.body.limit;

  var currencyRE = new RegExp(opts.currency ? ('^' + opts.currency.toUpperCase() + '$') : /./);

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: account' });
    }

    if (opts.counterparty && !ripple.UInt160.is_valid(opts.counterparty)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid Ripple address: issuer' });
    }

    if (opts.currency && !/^[A-Z0-9]{3}$/.test(opts.currency)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid currency: currency' });
    }

    callback();
  };

 function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function getAccountLines(connected, callback) {
    if (!connected) {
      return res.json(500, { success: false, message: 'Remote is not connected' });
    }

    var request = remote.requestAccountLines(opts.account);

    if (opts.counterparty) {
      request.message.peer = opts.counterparty;
    }

    request.once('error', callback);

    request.once('success', function(res) {
      var lines = [ ];

      res.lines.forEach(function(line) {
        if (!currencyRE.test(line.currency)) return;
        lines.push({
          account: opts.account,
          counterparty: line.account,
          currency: line.currency,
          trust_limit: line.limit,
          reciprocated_trust_limit: line.limit_peer,
          account_allows_rippling: line.no_ripple ? !line.no_ripple : true,
          counterparty_allows_rippling: line.no_ripple_peer ? !line.no_ripple_peer : true,
        });
      });

      callback(null, lines);
    });

    request.request();
  };

  var steps = [
    validateOptions,
    ensureConnected,
    getAccountLines
  ]

  async.waterfall(steps, function(err, lines) {
    if (err) {
      next(err);
    } else {
      res.json(200, { success: true, lines: lines });
    }
  });
};

exports.add = addTrustLine;

function addTrustLine($, req, res, next) {
  var self = this;
  var remote = $.remote;
  var opts = req.params;

  Object.keys(req.body).forEach(function(param) {
    opts[param] = req.body[param];
  });

  function validateOptions(callback) {
    if (!ripple.UInt160.is_valid(opts.account)) {
      return res.json(400, { success: false, message: 'Parameter is not a Ripple address: account' });
    }

    if (typeof opts.secret !== 'string') {
      return res.json(400, { success: false, message: 'Parameter is not a Ripple secret: secret' });
    }

    if (typeof opts.limit === 'string') {
      var spl = opts.limit.split('/');
      opts.limit = {
        amount:        spl[0],
        currency:      spl[1],
        counterparty:  spl[2]
      }
    }

    if (typeof opts.limit !== 'object') {
      return res.json(400, { success: false, message: 'Parameter is not a valid limit: limit' });
    }

    if (isNaN(opts.limit.amount)) {
      return res.json(400, { success: false, message: 'Parameter is not a number: limit.amount' });
    }

    if (!/^[A-Z0-9]{3}$/.test(opts.limit.currency)) {
      return res.json(400, { success: false, message: 'Parameter is not a valid currency: limit.currency' });
    }

    if (!ripple.UInt160.is_valid(opts.limit.counterparty)) {
      return res.json(400, { success: false, message: 'Parameter is not a Ripple address: limit.counterparty' });
    }

    if (!/^(undefined|number)$/.test(typeof opts.quality_in)) {
      return res.json(400, { success: false, message: 'Parameter must be a number: quality_in' });
    }

    if (!/^(undefined|number)$/.test(typeof opts.quality_out)) {
      return res.json(400, { success: false, message: 'Parameter must be a number: quality_out' });
    }

    if (!/^(undefined|boolean)$/.test(typeof opts.allow_rippling)) {
      return res.json(400, { success: false, message: 'Parameter must be a boolean: allow_rippling' });
    }

    callback();
  };

  function ensureConnected(callback) {
    serverLib.ensureConnected(remote, callback);
  };

  function addLine(connected, callback) {
    if (!connected) {
      return res.json(500, { success: false, message: 'Remote is not connected' });
    }

    var domain = Domain.create();

    domain.once('error', callback);

    domain.run(function() {
      var limit = [ opts.limit.amount, opts.limit.currency, opts.limit.counterparty ].join('/');
      var transaction = remote.transaction().trustSet(opts.account, limit);

      transaction.secret(opts.secret);

      if (typeof opts.quality_in === 'number') {
        transaction.tx_json.QualityIn = opts.quality_in;
      }

      if (typeof opts.quality_out === 'number') {
        transaction.tx_json.QualityOut = opts.quality_out;
      }

      if (typeof opts.allow_rippling === 'boolean') {
        if (opts.allow_rippling) {
          transaction.setFlags('ClearNoRipple');
        } else {
          transaction.setFlags('NoRipple');
        }
      }

      transaction.once('error', callback);

      transaction.once('proposed', function(m) {
        var summary = transaction.summary();

        var result = {
          success: true,
          line: {
            account: opts.account,
            counterparty: opts.limit.counterparty,
            currency: opts.limit.currency,
            trust_limit: opts.limit.amount,
            allows_rippling: true
          }
        }

        if (m.tx_json.Flags & TrustSetFlags.NoRipple.value) {
          result.line.allows_rippling = false;
        }

        if (m.tx_json.Flags & TrustSetFlags.SetAuth) {
          result.line.authorized = true;
        }

        result.ledger = String(summary.submitIndex);
        result.hash = m.tx_json.hash;

        callback(null, result);
      });

      transaction.submit();
    });
  };

  var steps = [
    validateOptions,
    ensureConnected,
    addLine
  ]

  async.waterfall(steps, function(err, line) {
    if (err) {
      next(err);
    } else {
      res.json(200, line);
    }
  });
};
<|endoftext|>


<commit_before>Facebook = {};

var querystring = Npm.require('querystring');

Facebook.handleAuthFromAccessToken = function handleAuthFromAccessToken(accessToken, expiresAt) {
  // include all fields from facebook
  // http://developers.facebook.com/docs/reference/login/public-profile-and-friend-list/
  var whitelisted = ['id', 'email', 'name', 'first_name',
      'last_name', 'link', 'gender', 'locale', 'age_range'];

  var identity = getIdentity(accessToken, whitelisted);

  var serviceData = {
    accessToken: accessToken,
    expiresAt: expiresAt
  };

  var fields = _.pick(identity, whitelisted);
  _.extend(serviceData, fields);

  return {
    serviceData: serviceData,
    options: {profile: {name: identity.name}}
  };
};

OAuth.registerService('facebook', 2, null, function(query) {
  var response = getTokenResponse(query);
  var accessToken = response.accessToken;
  var expiresIn = response.expiresIn;

  return Facebook.handleAuthFromAccessToken(accessToken, (+new Date) + (1000 * expiresIn));
});

// checks whether a string parses as JSON
var isJSON = function (str) {
  try {
    JSON.parse(str);
    return true;
  } catch (e) {
    return false;
  }
};

// returns an object containing:
// - accessToken
// - expiresIn: lifetime of token in seconds
var getTokenResponse = function (query) {
  var config = ServiceConfiguration.configurations.findOne({service: 'facebook'});
  if (!config)
    throw new ServiceConfiguration.ConfigError();

  var responseContent;
  try {
    // Request an access token
    responseContent = HTTP.get(
      "https://graph.facebook.com/v2.2/oauth/access_token", {
        params: {
          client_id: config.appId,
          redirect_uri: OAuth._redirectUri('facebook', config),
          client_secret: OAuth.openSecret(config.secret),
          code: query.code
        }
      }).content;
  } catch (err) {
    throw _.extend(new Error("Failed to complete OAuth handshake with Facebook. " + err.message),
                   {response: err.response});
  }

  // If 'responseContent' parses as JSON, it is an error.
  // XXX which facebook error causes this behvaior?
  if (isJSON(responseContent)) {
    throw new Error("Failed to complete OAuth handshake with Facebook. " + responseContent);
  }

  // Success!  Extract the facebook access token and expiration
  // time from the response
  var parsedResponse = querystring.parse(responseContent);
  var fbAccessToken = parsedResponse.access_token;
  var fbExpires = parsedResponse.expires;

  if (!fbAccessToken) {
    throw new Error("Failed to complete OAuth handshake with facebook " +
                    "-- can't find access token in HTTP response. " + responseContent);
  }
  return {
    accessToken: fbAccessToken,
    expiresIn: fbExpires
  };
};

var getIdentity = function (accessToken, fields) {
  try {
    return HTTP.get("https://graph.facebook.com/v2.4/me", {
      params: {
        access_token: accessToken,
        fields: fields
      }
    }).data;
  } catch (err) {
    throw _.extend(new Error("Failed to fetch identity from Facebook. " + err.message),
                   {response: err.response});
  }
};

Facebook.retrieveCredential = function(credentialToken, credentialSecret) {
  return OAuth.retrieveCredential(credentialToken, credentialSecret);
};
<commit_msg>[backport] Upgrade Facebook Graph API to use v2.8<commit_after>Facebook = {};

Facebook.handleAuthFromAccessToken = function handleAuthFromAccessToken(accessToken, expiresAt) {
  // include all fields from facebook
  // http://developers.facebook.com/docs/reference/login/public-profile-and-friend-list/
  var whitelisted = ['id', 'email', 'name', 'first_name',
      'last_name', 'link', 'gender', 'locale', 'age_range'];

  var identity = getIdentity(accessToken, whitelisted);

  var serviceData = {
    accessToken: accessToken,
    expiresAt: expiresAt
  };

  var fields = _.pick(identity, whitelisted);
  _.extend(serviceData, fields);

  return {
    serviceData: serviceData,
    options: {profile: {name: identity.name}}
  };
};

OAuth.registerService('facebook', 2, null, function(query) {
  var response = getTokenResponse(query);
  var accessToken = response.accessToken;
  var expiresIn = response.expiresIn;

  return Facebook.handleAuthFromAccessToken(accessToken, (+new Date) + (1000 * expiresIn));
});

// checks whether a string parses as JSON
var isJSON = function (str) {
  try {
    JSON.parse(str);
    return true;
  } catch (e) {
    return false;
  }
};

// returns an object containing:
// - accessToken
// - expiresIn: lifetime of token in seconds
var getTokenResponse = function (query) {
  var config = ServiceConfiguration.configurations.findOne({service: 'facebook'});
  if (!config)
    throw new ServiceConfiguration.ConfigError();

  var responseContent;
  try {
    // Request an access token
    responseContent = HTTP.get(
      "https://graph.facebook.com/v2.8/oauth/access_token", {
        params: {
          client_id: config.appId,
          redirect_uri: OAuth._redirectUri('facebook', config),
          client_secret: OAuth.openSecret(config.secret),
          code: query.code
        }
      }).data;
  } catch (err) {
    throw _.extend(new Error("Failed to complete OAuth handshake with Facebook. " + err.message),
                   {response: err.response});
  }

  var fbAccessToken = responseContent.access_token;
  var fbExpires = responseContent.expires_in;

  if (!fbAccessToken) {
    throw new Error("Failed to complete OAuth handshake with facebook " +
                    "-- can't find access token in HTTP response. " + responseContent);
  }
  return {
    accessToken: fbAccessToken,
    expiresIn: fbExpires
  };
};

var getIdentity = function (accessToken, fields) {
  try {
    return HTTP.get("https://graph.facebook.com/v2.8/me", {
      params: {
        access_token: accessToken,
        fields: fields
      }
    }).data;
  } catch (err) {
    throw _.extend(new Error("Failed to fetch identity from Facebook. " + err.message),
                   {response: err.response});
  }
};

Facebook.retrieveCredential = function(credentialToken, credentialSecret) {
  return OAuth.retrieveCredential(credentialToken, credentialSecret);
};
<|endoftext|>


<commit_before>
import svg from 'vd';

const title = 'slack';
const color = '#E01563';
const pad = 8; // left / right padding
const sep = 4; // middle separation

export default function badge({ total, active }){
  let value = active ? `${active}/${total}` : (total || '–');
  let lw = pad + width(title) + sep; // left side width
  let rw = sep + width(value) + pad; // right side width
  let tw = lw + rw; // total width

  return svg(`svg xmlns="http://www.w3.org/2000/svg" width=${tw} height=${tw}`,
    svg(`rect rx=3 width=${tw} height=20 fill=#555`),
    svg(`rect rx=3 x=${lw} width=${rw} height=20 fill=${color}`),
    svg(`path d="M${lw} 0h${sep}v20h-${sep}z" fill=${color}`),
    svg(`rect rx=3 width=${tw} height=20 fill=url(#g)`),
    svg('g text-anchor=middle font-family=Verdana font-size=11',
      text({ str: title, x: Math.round(lw / 2), y: 14 }),
      text({ str: value, x: lw + Math.round(rw / 2), y: 14 })
    )
  );
}

// generate text with 1px shadow
function text({str, x, y}){
  return [
    svg(`text fill=#010101 fill-opacity=.3 x=${x} y=${y + 1}`, str),
    svg(`text fill=#fff x=${x} y=${y}`, str)
  ];
}

// π=3
function width(str){
  return 7 * str.length;
}
<commit_msg>svg height should be fixed, not equal to the width. fixes #24<commit_after>
import svg from 'vd';

const title = 'slack';
const color = '#E01563';
const pad = 8; // left / right padding
const sep = 4; // middle separation

export default function badge({ total, active }){
  let value = active ? `${active}/${total}` : (total || '–');
  let lw = pad + width(title) + sep; // left side width
  let rw = sep + width(value) + pad; // right side width
  let tw = lw + rw; // total width

  return svg(`svg xmlns="http://www.w3.org/2000/svg" width=${tw} height=20`,
    svg(`rect rx=3 width=${tw} height=20 fill=#555`),
    svg(`rect rx=3 x=${lw} width=${rw} height=20 fill=${color}`),
    svg(`path d="M${lw} 0h${sep}v20h-${sep}z" fill=${color}`),
    svg(`rect rx=3 width=${tw} height=20 fill=url(#g)`),
    svg('g text-anchor=middle font-family=Verdana font-size=11',
      text({ str: title, x: Math.round(lw / 2), y: 14 }),
      text({ str: value, x: lw + Math.round(rw / 2), y: 14 })
    )
  );
}

// generate text with 1px shadow
function text({str, x, y}){
  return [
    svg(`text fill=#010101 fill-opacity=.3 x=${x} y=${y + 1}`, str),
    svg(`text fill=#fff x=${x} y=${y}`, str)
  ];
}

// π=3
function width(str){
  return 7 * str.length;
}
<|endoftext|>


<commit_before>const _ = require(`lodash`)
const invariant = require(`invariant`)
const {
  isSpecifiedScalarType,
  isIntrospectionType,
  defaultFieldResolver,
  assertValidName,
} = require(`graphql`)
const apiRunner = require(`../utils/api-runner-node`)
const report = require(`gatsby-cli/lib/reporter`)
const { addNodeInterfaceFields } = require(`./types/node-interface`)
const { addInferredType, addInferredTypes } = require(`./infer`)
const { findOne, findManyPaginated } = require(`./resolvers`)
const { getPagination } = require(`./types/pagination`)
const { getSortInput } = require(`./types/sort`)
const { getFilterInput } = require(`./types/filter`)
const { isGatsbyType, GatsbyGraphQLTypeKind } = require(`./types/type-builders`)

const buildSchema = async ({
  schemaComposer,
  nodeStore,
  types,
  thirdPartySchemas,
  typeMapping,
  typeConflictReporter,
  parentSpan,
}) => {
  await updateSchemaComposer({
    schemaComposer,
    nodeStore,
    types,
    thirdPartySchemas,
    typeMapping,
    typeConflictReporter,
    parentSpan,
  })
  // const { printSchema } = require(`graphql`)
  const schema = schemaComposer.buildSchema()
  // console.log(printSchema(schema))
  return schema
}

const rebuildSchemaWithSitePage = async ({
  schemaComposer,
  nodeStore,
  typeMapping,
  typeConflictReporter,
  parentSpan,
}) => {
  const typeComposer = addInferredType({
    schemaComposer,
    typeComposer: schemaComposer.getTC(`SitePage`),
    nodeStore,
    typeConflictReporter,
    typeMapping,
    parentSpan,
  })
  await processTypeComposer({
    schemaComposer,
    typeComposer,
    nodeStore,
    parentSpan,
  })
  return schemaComposer.buildSchema()
}

module.exports = {
  buildSchema,
  rebuildSchemaWithSitePage,
}

const updateSchemaComposer = async ({
  schemaComposer,
  nodeStore,
  types,
  typeMapping,
  thirdPartySchemas,
  typeConflictReporter,
  parentSpan,
}) => {
  await addTypes({ schemaComposer, parentSpan, types })
  await addInferredTypes({
    schemaComposer,
    nodeStore,
    typeConflictReporter,
    typeMapping,
    parentSpan,
  })
  await addSetFieldsOnGraphQLNodeTypeFields({
    schemaComposer,
    nodeStore,
    parentSpan,
  })
  await Promise.all(
    Array.from(schemaComposer.values()).map(typeComposer =>
      processTypeComposer({
        schemaComposer,
        typeComposer,
        nodeStore,
        parentSpan,
      })
    )
  )
  await addThirdPartySchemas({ schemaComposer, thirdPartySchemas, parentSpan })
  await addCustomResolveFunctions({ schemaComposer, parentSpan })
}

const processTypeComposer = async ({
  schemaComposer,
  typeComposer,
  nodeStore,
  parentSpan,
}) => {
  if (
    typeComposer instanceof schemaComposer.TypeComposer &&
    typeComposer.hasInterface(`Node`)
  ) {
    await addNodeInterfaceFields({ schemaComposer, typeComposer, parentSpan })
    await addResolvers({ schemaComposer, typeComposer, parentSpan })
    await addConvenienceChildrenFields({
      schemaComposer,
      typeComposer,
      nodeStore,
      parentSpan,
    })
    await addTypeToRootQuery({ schemaComposer, typeComposer, parentSpan })
  }
}

const addTypes = ({ schemaComposer, types, parentSpan }) => {
  types.forEach(typeOrTypeDef => {
    if (typeof typeOrTypeDef === `string`) {
      let addedTypes
      try {
        addedTypes = schemaComposer.addTypeDefs(typeOrTypeDef)
      } catch (error) {
        reportParsingError(error)
      }
      addedTypes.forEach(type =>
        processAddedType({ schemaComposer, type, parentSpan })
      )
    } else if (isGatsbyType(typeOrTypeDef)) {
      const type = createTypeComposerFromGatsbyType({
        schemaComposer,
        type: typeOrTypeDef,
        parentSpan,
      })
      if (type) {
        processAddedType({ schemaComposer, type, parentSpan })
      }
    } else {
      processAddedType({ schemaComposer, type: typeOrTypeDef, parentSpan })
    }
  })
}

const processAddedType = ({ schemaComposer, type, parentSpan }) => {
  const typeName = schemaComposer.addAsComposer(type)
  checkIsAllowedTypeName(typeName)
  const typeComposer = schemaComposer.get(typeName)
  if (
    typeComposer instanceof schemaComposer.InterfaceTypeComposer ||
    typeComposer instanceof schemaComposer.UnionTypeComposer
  ) {
    if (!typeComposer.getResolveType()) {
      typeComposer.setResolveType(node => node.internal.type)
    }
  }
  schemaComposer.addSchemaMustHaveType(typeComposer)
}

const checkIsAllowedTypeName = name => {
  invariant(
    name !== `Node`,
    `The GraphQL type \`Node\` is reserved for internal use.`
  )
  invariant(
    !name.endsWith(`FilterInput`) && !name.endsWith(`SortInput`),
    `GraphQL type names ending with "FilterInput" or "SortInput" are ` +
      `reserved for internal use. Please rename \`${name}\`.`
  )
  invariant(
    ![`Boolean`, `Date`, `Float`, `ID`, `Int`, `JSON`, `String`].includes(name),
    `The GraphQL type \`${name}\` is reserved for internal use by ` +
      `built-in scalar types.`
  )
  assertValidName(name)
}

const createTypeComposerFromGatsbyType = ({
  schemaComposer,
  type,
  parentSpan,
}) => {
  switch (type.kind) {
    case GatsbyGraphQLTypeKind.OBJECT: {
      return schemaComposer.TypeComposer.createTemp({
        ...type.config,
        interfaces: () => {
          if (type.config.interfaces) {
            return type.config.interfaces.map(iface => {
              if (typeof iface === `string`) {
                return schemaComposer.getIFTC(iface).getType()
              } else {
                return iface
              }
            })
          } else {
            return []
          }
        },
      })
    }
    case GatsbyGraphQLTypeKind.INPUT_OBJECT: {
      return schemaComposer.InputTypeComposer.createTemp(type.config)
    }
    case GatsbyGraphQLTypeKind.UNION: {
      return schemaComposer.UnionTypeComposer.createTemp({
        ...type.config,
        types: () => {
          if (type.types) {
            return type.types.map(typeName =>
              schemaComposer.getTC(typeName).getType()
            )
          } else {
            return []
          }
        },
      })
    }
    case GatsbyGraphQLTypeKind.INTERFACE: {
      return schemaComposer.InterfaceTypeComposer.createTemp(type.config)
    }
    default: {
      console.warn(`Illegal type definition: ${JSON.stringify(type.config)}`)
      return null
    }
  }
}

const addSetFieldsOnGraphQLNodeTypeFields = ({
  schemaComposer,
  nodeStore,
  parentSpan,
}) =>
  Promise.all(
    Array.from(schemaComposer.values()).map(async tc => {
      if (
        tc instanceof schemaComposer.TypeComposer &&
        tc.hasInterface(`Node`)
      ) {
        const typeName = tc.getTypeName()
        const result = await apiRunner(`setFieldsOnGraphQLNodeType`, {
          type: {
            name: typeName,
            nodes: nodeStore.getNodesByType(typeName),
          },
          traceId: `initial-setFieldsOnGraphQLNodeType`,
          parentSpan: parentSpan,
        })
        if (result) {
          // NOTE: `setFieldsOnGraphQLNodeType` only allows setting
          // nested fields with a path as property name, i.e.
          // `{ 'frontmatter.published': 'Boolean' }`, but not in the form
          // `{ frontmatter: { published: 'Boolean' }}`
          result.forEach(fields => tc.addNestedFields(fields))
        }
      }
    })
  )

const addThirdPartySchemas = ({
  schemaComposer,
  thirdPartySchemas,
  parentSpan,
}) => {
  thirdPartySchemas.forEach(schema => {
    const queryTC = schemaComposer.TypeComposer.createTemp(
      schema.getQueryType()
    )
    const fields = queryTC.getFields()
    schemaComposer.Query.addFields(fields)

    // Explicitly add the third-party schema's types, so they can be targeted
    // in `createResolvers` API.
    const types = schema.getTypeMap()
    Object.keys(types).forEach(typeName => {
      const type = types[typeName]
      if (
        type !== schema.getQueryType() &&
        !isSpecifiedScalarType(type) &&
        !isIntrospectionType(type)
      ) {
        type.isThirdPartyType = true
        schemaComposer.add(type)
      }
    })
  })
}

const addCustomResolveFunctions = async ({ schemaComposer, parentSpan }) => {
  const intermediateSchema = schemaComposer.buildSchema()
  const createResolvers = resolvers => {
    Object.keys(resolvers).forEach(typeName => {
      const fields = resolvers[typeName]
      if (schemaComposer.has(typeName)) {
        const tc = schemaComposer.getTC(typeName)
        Object.keys(fields).forEach(fieldName => {
          const fieldConfig = fields[fieldName]
          if (tc.hasField(fieldName)) {
            const originalFieldConfig = tc.getFieldConfig(fieldName)
            const originalTypeName = originalFieldConfig.type.toString()
            const originalResolver = originalFieldConfig.resolve
            const fieldTypeName =
              fieldConfig.type && fieldConfig.type.toString()
            if (
              !fieldTypeName ||
              tc.getFieldType(fieldName) === fieldConfig.type.toString() ||
              tc.getType().isThirdPartyType
            ) {
              const newConfig = {}
              if (fieldConfig.type) {
                newConfig.type = fieldConfig.type
              }
              if (fieldConfig.args) {
                newConfig.args = fieldConfig.args
              }
              if (fieldConfig.resolve) {
                newConfig.resolve = (source, args, context, info) =>
                  fieldConfig.resolve(source, args, context, {
                    ...info,
                    originalResolver: originalResolver || defaultFieldResolver,
                  })
              }
              tc.extendField(fieldName, newConfig)
            } else if (fieldTypeName) {
              report.warn(
                `\`createResolvers\` passed resolvers for field ` +
                  `\`${typeName}.${fieldName}\` with type \`${fieldTypeName}\`. ` +
                  `Such a field with type \`${originalTypeName}\` already exists ` +
                  `on the type. Use \`createTypes\` to override type fields.`
              )
            }
          } else {
            tc.addFields({ [fieldName]: fieldConfig })
          }
        })
      } else {
        report.warn(
          `\`createResolvers\` passed resolvers for type \`${typeName}\` that ` +
            `doesn't exist in the schema. Use \`createTypes\` to add the type ` +
            `before adding resolvers.`
        )
      }
    })
  }
  await apiRunner(`createResolvers`, {
    schema: intermediateSchema,
    createResolvers,
    traceId: `initial-createResolvers`,
    parentSpan: parentSpan,
  })
}

const addResolvers = ({ schemaComposer, typeComposer }) => {
  const typeName = typeComposer.getTypeName()

  // TODO: We should have an abstraction for keeping and clearing
  // related TypeComposers and InputTypeComposers.
  // Also see the comment on the skipped test in `rebuild-schema`.
  typeComposer.removeInputTypeComposer()

  const sortInputTC = getSortInput({
    schemaComposer,
    typeComposer,
  })
  const filterInputTC = getFilterInput({
    schemaComposer,
    typeComposer,
  })
  const paginationTC = getPagination({
    schemaComposer,
    typeComposer,
  })
  typeComposer.addResolver({
    name: `findOne`,
    type: typeComposer,
    args: {
      ...filterInputTC.getFields(),
    },
    resolve: findOne(typeName),
  })
  typeComposer.addResolver({
    name: `findManyPaginated`,
    type: paginationTC,
    args: {
      filter: filterInputTC,
      sort: sortInputTC,
      skip: `Int`,
      limit: `Int`,
      // page: `Int`,
      // perPage: { type: `Int`, defaultValue: 20 },
    },
    resolve: findManyPaginated(typeName),
  })
}

const addConvenienceChildrenFields = ({
  schemaComposer,
  typeComposer,
  nodeStore,
}) => {
  const nodes = nodeStore.getNodesByType(typeComposer.getTypeName())

  const childNodesByType = groupChildNodesByType({ nodeStore, nodes })

  Object.keys(childNodesByType).forEach(typeName => {
    const typeChildren = childNodesByType[typeName]
    const maxChildCount = _.maxBy(
      _.values(_.groupBy(typeChildren, c => c.parent)),
      g => g.length
    ).length

    if (maxChildCount > 1) {
      typeComposer.addFields(createChildrenField(typeName))
    } else {
      typeComposer.addFields(createChildField(typeName))
    }
  })
}

function createChildrenField(typeName) {
  return {
    [_.camelCase(`children ${typeName}`)]: {
      type: () => [typeName],
      resolve(source, args, context) {
        const { path } = context
        return context.nodeModel.getNodesByIds(
          { ids: source.children, type: typeName },
          { path }
        )
      },
    },
  }
}

function createChildField(typeName) {
  return {
    [_.camelCase(`child ${typeName}`)]: {
      type: () => typeName,
      async resolve(source, args, context) {
        const { path } = context
        const result = await context.nodeModel.getNodesByIds(
          { ids: source.children, type: typeName },
          { path }
        )
        if (result && result.length > 0) {
          return result[0]
        } else {
          return null
        }
      },
    },
  }
}

function groupChildNodesByType({ nodeStore, nodes }) {
  return _(nodes)
    .flatMap(node => (node.children || []).map(nodeStore.getNode))
    .groupBy(node => (node.internal ? node.internal.type : undefined))
    .value()
}

const addTypeToRootQuery = ({ schemaComposer, typeComposer }) => {
  const typeName = typeComposer.getTypeName()
  // not strictly correctly, result is `npmPackage` and `allNpmPackage` from type `NPMPackage`
  const queryName = _.camelCase(typeName)
  const queryNamePlural = _.camelCase(`all ${typeName}`)
  schemaComposer.Query.addFields({
    [queryName]: typeComposer.getResolver(`findOne`),
    [queryNamePlural]: typeComposer.getResolver(`findManyPaginated`),
  })
}

const reportParsingError = error => {
  const { message, source, locations } = error

  if (source && locations && locations.length) {
    const report = require(`gatsby-cli/lib/reporter`)
    const { codeFrameColumns } = require(`@babel/code-frame`)

    const frame = codeFrameColumns(
      source.body,
      { start: locations[0] },
      { linesAbove: 5, linesBelow: 5 }
    )
    report.panic(
      `Encountered an error parsing the provided GraphQL type definitions:\n` +
        message +
        `\n\n` +
        frame +
        `\n`
    )
  } else {
    throw error
  }
}
<commit_msg>fix(gatsby): Fix third-party schemas with self-referential queries failing (#12668)<commit_after>const _ = require(`lodash`)
const invariant = require(`invariant`)
const {
  isSpecifiedScalarType,
  isIntrospectionType,
  defaultFieldResolver,
  assertValidName,
  getNamedType,
} = require(`graphql`)
const apiRunner = require(`../utils/api-runner-node`)
const report = require(`gatsby-cli/lib/reporter`)
const { addNodeInterfaceFields } = require(`./types/node-interface`)
const { addInferredType, addInferredTypes } = require(`./infer`)
const { findOne, findManyPaginated } = require(`./resolvers`)
const { getPagination } = require(`./types/pagination`)
const { getSortInput } = require(`./types/sort`)
const { getFilterInput } = require(`./types/filter`)
const { isGatsbyType, GatsbyGraphQLTypeKind } = require(`./types/type-builders`)

const buildSchema = async ({
  schemaComposer,
  nodeStore,
  types,
  thirdPartySchemas,
  typeMapping,
  typeConflictReporter,
  parentSpan,
}) => {
  await updateSchemaComposer({
    schemaComposer,
    nodeStore,
    types,
    thirdPartySchemas,
    typeMapping,
    typeConflictReporter,
    parentSpan,
  })
  // const { printSchema } = require(`graphql`)
  const schema = schemaComposer.buildSchema()
  // console.log(printSchema(schema))
  return schema
}

const rebuildSchemaWithSitePage = async ({
  schemaComposer,
  nodeStore,
  typeMapping,
  typeConflictReporter,
  parentSpan,
}) => {
  const typeComposer = addInferredType({
    schemaComposer,
    typeComposer: schemaComposer.getTC(`SitePage`),
    nodeStore,
    typeConflictReporter,
    typeMapping,
    parentSpan,
  })
  await processTypeComposer({
    schemaComposer,
    typeComposer,
    nodeStore,
    parentSpan,
  })
  return schemaComposer.buildSchema()
}

module.exports = {
  buildSchema,
  rebuildSchemaWithSitePage,
}

const updateSchemaComposer = async ({
  schemaComposer,
  nodeStore,
  types,
  typeMapping,
  thirdPartySchemas,
  typeConflictReporter,
  parentSpan,
}) => {
  await addTypes({ schemaComposer, parentSpan, types })
  await addInferredTypes({
    schemaComposer,
    nodeStore,
    typeConflictReporter,
    typeMapping,
    parentSpan,
  })
  await addSetFieldsOnGraphQLNodeTypeFields({
    schemaComposer,
    nodeStore,
    parentSpan,
  })
  await Promise.all(
    Array.from(schemaComposer.values()).map(typeComposer =>
      processTypeComposer({
        schemaComposer,
        typeComposer,
        nodeStore,
        parentSpan,
      })
    )
  )
  await addThirdPartySchemas({ schemaComposer, thirdPartySchemas, parentSpan })
  await addCustomResolveFunctions({ schemaComposer, parentSpan })
}

const processTypeComposer = async ({
  schemaComposer,
  typeComposer,
  nodeStore,
  parentSpan,
}) => {
  if (
    typeComposer instanceof schemaComposer.TypeComposer &&
    typeComposer.hasInterface(`Node`)
  ) {
    await addNodeInterfaceFields({ schemaComposer, typeComposer, parentSpan })
    await addResolvers({ schemaComposer, typeComposer, parentSpan })
    await addConvenienceChildrenFields({
      schemaComposer,
      typeComposer,
      nodeStore,
      parentSpan,
    })
    await addTypeToRootQuery({ schemaComposer, typeComposer, parentSpan })
  }
}

const addTypes = ({ schemaComposer, types, parentSpan }) => {
  types.forEach(typeOrTypeDef => {
    if (typeof typeOrTypeDef === `string`) {
      let addedTypes
      try {
        addedTypes = schemaComposer.addTypeDefs(typeOrTypeDef)
      } catch (error) {
        reportParsingError(error)
      }
      addedTypes.forEach(type =>
        processAddedType({ schemaComposer, type, parentSpan })
      )
    } else if (isGatsbyType(typeOrTypeDef)) {
      const type = createTypeComposerFromGatsbyType({
        schemaComposer,
        type: typeOrTypeDef,
        parentSpan,
      })
      if (type) {
        processAddedType({ schemaComposer, type, parentSpan })
      }
    } else {
      processAddedType({ schemaComposer, type: typeOrTypeDef, parentSpan })
    }
  })
}

const processAddedType = ({ schemaComposer, type, parentSpan }) => {
  const typeName = schemaComposer.addAsComposer(type)
  checkIsAllowedTypeName(typeName)
  const typeComposer = schemaComposer.get(typeName)
  if (
    typeComposer instanceof schemaComposer.InterfaceTypeComposer ||
    typeComposer instanceof schemaComposer.UnionTypeComposer
  ) {
    if (!typeComposer.getResolveType()) {
      typeComposer.setResolveType(node => node.internal.type)
    }
  }
  schemaComposer.addSchemaMustHaveType(typeComposer)
}

const checkIsAllowedTypeName = name => {
  invariant(
    name !== `Node`,
    `The GraphQL type \`Node\` is reserved for internal use.`
  )
  invariant(
    !name.endsWith(`FilterInput`) && !name.endsWith(`SortInput`),
    `GraphQL type names ending with "FilterInput" or "SortInput" are ` +
      `reserved for internal use. Please rename \`${name}\`.`
  )
  invariant(
    ![`Boolean`, `Date`, `Float`, `ID`, `Int`, `JSON`, `String`].includes(name),
    `The GraphQL type \`${name}\` is reserved for internal use by ` +
      `built-in scalar types.`
  )
  assertValidName(name)
}

const createTypeComposerFromGatsbyType = ({
  schemaComposer,
  type,
  parentSpan,
}) => {
  switch (type.kind) {
    case GatsbyGraphQLTypeKind.OBJECT: {
      return schemaComposer.TypeComposer.createTemp({
        ...type.config,
        interfaces: () => {
          if (type.config.interfaces) {
            return type.config.interfaces.map(iface => {
              if (typeof iface === `string`) {
                return schemaComposer.getIFTC(iface).getType()
              } else {
                return iface
              }
            })
          } else {
            return []
          }
        },
      })
    }
    case GatsbyGraphQLTypeKind.INPUT_OBJECT: {
      return schemaComposer.InputTypeComposer.createTemp(type.config)
    }
    case GatsbyGraphQLTypeKind.UNION: {
      return schemaComposer.UnionTypeComposer.createTemp({
        ...type.config,
        types: () => {
          if (type.types) {
            return type.types.map(typeName =>
              schemaComposer.getTC(typeName).getType()
            )
          } else {
            return []
          }
        },
      })
    }
    case GatsbyGraphQLTypeKind.INTERFACE: {
      return schemaComposer.InterfaceTypeComposer.createTemp(type.config)
    }
    default: {
      console.warn(`Illegal type definition: ${JSON.stringify(type.config)}`)
      return null
    }
  }
}

const addSetFieldsOnGraphQLNodeTypeFields = ({
  schemaComposer,
  nodeStore,
  parentSpan,
}) =>
  Promise.all(
    Array.from(schemaComposer.values()).map(async tc => {
      if (
        tc instanceof schemaComposer.TypeComposer &&
        tc.hasInterface(`Node`)
      ) {
        const typeName = tc.getTypeName()
        const result = await apiRunner(`setFieldsOnGraphQLNodeType`, {
          type: {
            name: typeName,
            nodes: nodeStore.getNodesByType(typeName),
          },
          traceId: `initial-setFieldsOnGraphQLNodeType`,
          parentSpan: parentSpan,
        })
        if (result) {
          // NOTE: `setFieldsOnGraphQLNodeType` only allows setting
          // nested fields with a path as property name, i.e.
          // `{ 'frontmatter.published': 'Boolean' }`, but not in the form
          // `{ frontmatter: { published: 'Boolean' }}`
          result.forEach(fields => tc.addNestedFields(fields))
        }
      }
    })
  )

const addThirdPartySchemas = ({
  schemaComposer,
  thirdPartySchemas,
  parentSpan,
}) => {
  thirdPartySchemas.forEach(schema => {
    const schemaQueryType = schema.getQueryType()
    const queryTC = schemaComposer.TypeComposer.createTemp(schemaQueryType)
    const fields = queryTC.getFields()
    schemaComposer.Query.addFields(fields)

    // Explicitly add the third-party schema's types, so they can be targeted
    // in `createResolvers` API.
    const types = schema.getTypeMap()
    Object.keys(types).forEach(typeName => {
      const type = types[typeName]
      if (
        type !== schemaQueryType &&
        !isSpecifiedScalarType(type) &&
        !isIntrospectionType(type)
      ) {
        type.isThirdPartyType = true
        const typeComposer = schemaComposer.TypeComposer.createTemp(type)
        typeComposer.getFieldNames().forEach(fieldName => {
          const fieldType = typeComposer.getFieldType(fieldName)
          if (getNamedType(fieldType) === schemaQueryType) {
            typeComposer.extendField(fieldName, {
              type: `Query`,
            })
          }
        })
        schemaComposer.add(typeComposer)
      }
    })
  })
}

const addCustomResolveFunctions = async ({ schemaComposer, parentSpan }) => {
  const intermediateSchema = schemaComposer.buildSchema()
  const createResolvers = resolvers => {
    Object.keys(resolvers).forEach(typeName => {
      const fields = resolvers[typeName]
      if (schemaComposer.has(typeName)) {
        const tc = schemaComposer.getTC(typeName)
        Object.keys(fields).forEach(fieldName => {
          const fieldConfig = fields[fieldName]
          if (tc.hasField(fieldName)) {
            const originalFieldConfig = tc.getFieldConfig(fieldName)
            const originalTypeName = originalFieldConfig.type.toString()
            const originalResolver = originalFieldConfig.resolve
            const fieldTypeName =
              fieldConfig.type && fieldConfig.type.toString()
            if (
              !fieldTypeName ||
              tc.getFieldType(fieldName) === fieldConfig.type.toString() ||
              tc.getType().isThirdPartyType
            ) {
              const newConfig = {}
              if (fieldConfig.type) {
                newConfig.type = fieldConfig.type
              }
              if (fieldConfig.args) {
                newConfig.args = fieldConfig.args
              }
              if (fieldConfig.resolve) {
                newConfig.resolve = (source, args, context, info) =>
                  fieldConfig.resolve(source, args, context, {
                    ...info,
                    originalResolver: originalResolver || defaultFieldResolver,
                  })
              }
              tc.extendField(fieldName, newConfig)
            } else if (fieldTypeName) {
              report.warn(
                `\`createResolvers\` passed resolvers for field ` +
                  `\`${typeName}.${fieldName}\` with type \`${fieldTypeName}\`. ` +
                  `Such a field with type \`${originalTypeName}\` already exists ` +
                  `on the type. Use \`createTypes\` to override type fields.`
              )
            }
          } else {
            tc.addFields({ [fieldName]: fieldConfig })
          }
        })
      } else {
        report.warn(
          `\`createResolvers\` passed resolvers for type \`${typeName}\` that ` +
            `doesn't exist in the schema. Use \`createTypes\` to add the type ` +
            `before adding resolvers.`
        )
      }
    })
  }
  await apiRunner(`createResolvers`, {
    schema: intermediateSchema,
    createResolvers,
    traceId: `initial-createResolvers`,
    parentSpan: parentSpan,
  })
}

const addResolvers = ({ schemaComposer, typeComposer }) => {
  const typeName = typeComposer.getTypeName()

  // TODO: We should have an abstraction for keeping and clearing
  // related TypeComposers and InputTypeComposers.
  // Also see the comment on the skipped test in `rebuild-schema`.
  typeComposer.removeInputTypeComposer()

  const sortInputTC = getSortInput({
    schemaComposer,
    typeComposer,
  })
  const filterInputTC = getFilterInput({
    schemaComposer,
    typeComposer,
  })
  const paginationTC = getPagination({
    schemaComposer,
    typeComposer,
  })
  typeComposer.addResolver({
    name: `findOne`,
    type: typeComposer,
    args: {
      ...filterInputTC.getFields(),
    },
    resolve: findOne(typeName),
  })
  typeComposer.addResolver({
    name: `findManyPaginated`,
    type: paginationTC,
    args: {
      filter: filterInputTC,
      sort: sortInputTC,
      skip: `Int`,
      limit: `Int`,
      // page: `Int`,
      // perPage: { type: `Int`, defaultValue: 20 },
    },
    resolve: findManyPaginated(typeName),
  })
}

const addConvenienceChildrenFields = ({
  schemaComposer,
  typeComposer,
  nodeStore,
}) => {
  const nodes = nodeStore.getNodesByType(typeComposer.getTypeName())

  const childNodesByType = groupChildNodesByType({ nodeStore, nodes })

  Object.keys(childNodesByType).forEach(typeName => {
    const typeChildren = childNodesByType[typeName]
    const maxChildCount = _.maxBy(
      _.values(_.groupBy(typeChildren, c => c.parent)),
      g => g.length
    ).length

    if (maxChildCount > 1) {
      typeComposer.addFields(createChildrenField(typeName))
    } else {
      typeComposer.addFields(createChildField(typeName))
    }
  })
}

function createChildrenField(typeName) {
  return {
    [_.camelCase(`children ${typeName}`)]: {
      type: () => [typeName],
      resolve(source, args, context) {
        const { path } = context
        return context.nodeModel.getNodesByIds(
          { ids: source.children, type: typeName },
          { path }
        )
      },
    },
  }
}

function createChildField(typeName) {
  return {
    [_.camelCase(`child ${typeName}`)]: {
      type: () => typeName,
      async resolve(source, args, context) {
        const { path } = context
        const result = await context.nodeModel.getNodesByIds(
          { ids: source.children, type: typeName },
          { path }
        )
        if (result && result.length > 0) {
          return result[0]
        } else {
          return null
        }
      },
    },
  }
}

function groupChildNodesByType({ nodeStore, nodes }) {
  return _(nodes)
    .flatMap(node => (node.children || []).map(nodeStore.getNode))
    .groupBy(node => (node.internal ? node.internal.type : undefined))
    .value()
}

const addTypeToRootQuery = ({ schemaComposer, typeComposer }) => {
  const typeName = typeComposer.getTypeName()
  // not strictly correctly, result is `npmPackage` and `allNpmPackage` from type `NPMPackage`
  const queryName = _.camelCase(typeName)
  const queryNamePlural = _.camelCase(`all ${typeName}`)
  schemaComposer.Query.addFields({
    [queryName]: typeComposer.getResolver(`findOne`),
    [queryNamePlural]: typeComposer.getResolver(`findManyPaginated`),
  })
}

const reportParsingError = error => {
  const { message, source, locations } = error

  if (source && locations && locations.length) {
    const report = require(`gatsby-cli/lib/reporter`)
    const { codeFrameColumns } = require(`@babel/code-frame`)

    const frame = codeFrameColumns(
      source.body,
      { start: locations[0] },
      { linesAbove: 5, linesBelow: 5 }
    )
    report.panic(
      `Encountered an error parsing the provided GraphQL type definitions:\n` +
        message +
        `\n\n` +
        frame +
        `\n`
    )
  } else {
    throw error
  }
}
<|endoftext|>


<commit_before>
module.exports = {
  webapp: webapp
}

function webapp(id, plugin, context, done) {
  if ('function' !== typeof plugin) {
    throw new Error('Invalid basic plugin: ', id, plugin)
  }
  plugin(context, done)
}
<commit_msg>make error string more useful<commit_after>
module.exports = {
  webapp: webapp
}

function webapp(id, plugin, context, done) {
  if ('function' !== typeof plugin) {
    throw new Error('Invalid basic plugin: ' + id + ' ' + plugin)
  }
  plugin(context, done)
}
<|endoftext|>


<commit_before>'use strict';

var Block = function (type) {
    this.start = 0;
    this.end = 0;
    this.type = type;
    this.files = [];
    this.linefeed = '\n';
    this.indent = '    ';
};

Block.prototype.getSize = function () {
    return this.end - this.start + 1;
};

Block.prototype.getReplacement = function () {
    var self = this;
    var parts = [];
    this.files.forEach(function (file) {
        var ext = file.split('.').pop().toLowerCase();
        if (ext === 'js') {
            parts.push(self.indent + '<script src="' + file + '"></script>');
        } else if (ext === 'css') {
            parts.push(self.indent + '<link rel="stylesheet" href="' + file + '">');
        }
    });

    return parts.join(this.linefeed);
};

module.exports = Block;<commit_msg>No indentation by default<commit_after>'use strict';

var Block = function (type) {
    this.start = 0;
    this.end = 0;
    this.type = type;
    this.files = [];
    this.linefeed = '\n';
    this.indent = '';
};

Block.prototype.getSize = function () {
    return this.end - this.start + 1;
};

Block.prototype.getReplacement = function () {
    var self = this;
    var parts = [];
    this.files.forEach(function (file) {
        var ext = file.split('.').pop().toLowerCase();
        if (ext === 'js') {
            parts.push(self.indent + '<script src="' + file + '"></script>');
        } else if (ext === 'css') {
            parts.push(self.indent + '<link rel="stylesheet" href="' + file + '">');
        }
    });

    return parts.join(this.linefeed);
};

module.exports = Block;<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var SubSocket = require('./sub');

/**
 * Expose `SubEmitterSocket`.
 */

module.exports = SubEmitterSocket;

/**
 * Initialzie a new `SubEmitterSocket`.
 *
 * @api private
 */

function SubEmitterSocket() {
  this.sock = new SubSocket;
  this.sock.format('json');
  this.sock.onmessage = this.onmessage.bind(this);
  this.listeners = [];
}

/**
 * Message handler.
 *
 * @param {net.Socket} sock
 * @return {Function} closure(msg, mulitpart)
 * @api private
 */

SubEmitterSocket.prototype.onmessage = function(){
  var self = this;
  var listeners = this.listeners;

  return function(msg, multipart){
    var topic = multipart
      ? msg[0].toString()
      : msg.toString();

    for (var i = 0; i < listeners.length; ++i) {
      var listener = listeners[i];
      var m = listener.re.exec(topic);
      if (!m) continue;
      if (multipart) {
        listener.fn.apply(this, m.slice(1).concat(msg.slice(1)));
      } else {
        listener.fn.apply(this, m.slice(1));
      }
    }
  }
};

/**
 * Subscribe to `event` and invoke the given callback `fn`.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {SubEmitterSocket} self
 * @api public
 */

SubEmitterSocket.prototype.on = function(event, fn){
  var re = this.sock.subscribe(event);
  this.listeners.push({
    event: event,
    re: re,
    fn: fn
  });
  return this;
};

/**
 * Bind, see `Socket#bind()`.
 *
 * @api public
 */

SubEmitterSocket.prototype.bind = function(){
  return this.sock.bind.apply(this.sock, arguments);
};

/**
 * Connect, see `Socket#connect()`.
 *
 * @api public
 */

SubEmitterSocket.prototype.connect = function(){
  return this.sock.connect.apply(this.sock, arguments);
};

/**
 * Close the sub socket.
 *
 * @api public
 */

SubEmitterSocket.prototype.close = function(){
  return this.sock.close();
};
<commit_msg>refactor SubEmitter<commit_after>
/**
 * Module dependencies.
 */

var SubSocket = require('./sub');

/**
 * Expose `SubEmitterSocket`.
 */

module.exports = SubEmitterSocket;

/**
 * Initialzie a new `SubEmitterSocket`.
 *
 * @api private
 */

function SubEmitterSocket() {
  this.sock = new SubSocket;
  this.sock.format('json');
  this.sock.onmessage = this.onmessage.bind(this);
  this.bind = this.sock.bind.bind(this.sock);
  this.connect = this.sock.connect.bind(this.sock);
  this.close = this.sock.close.bind(this.sock);
  this.listeners = [];
}

/**
 * Message handler.
 *
 * @param {net.Socket} sock
 * @return {Function} closure(msg, mulitpart)
 * @api private
 */

SubEmitterSocket.prototype.onmessage = function(){
  var self = this;
  var listeners = this.listeners;

  return function(msg, multipart){
    var topic = multipart
      ? msg[0].toString()
      : msg.toString();

    for (var i = 0; i < listeners.length; ++i) {
      var listener = listeners[i];
      var m = listener.re.exec(topic);
      if (!m) continue;
      if (multipart) {
        listener.fn.apply(this, m.slice(1).concat(msg.slice(1)));
      } else {
        listener.fn.apply(this, m.slice(1));
      }
    }
  }
};

/**
 * Subscribe to `event` and invoke the given callback `fn`.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {SubEmitterSocket} self
 * @api public
 */

SubEmitterSocket.prototype.on = function(event, fn){
  var re = this.sock.subscribe(event);
  this.listeners.push({
    event: event,
    re: re,
    fn: fn
  });
  return this;
};
<|endoftext|>


<commit_before>/*

    build.js

    Accepts:  config  - object, the config data
              files   - object, the files object generated in schema.js

    Creates a set of HTML files based on the json schemas and template
    files within the files object

*/

var fs  = require('fs');

module.exports = function (config, files) {

  // Import the template language
  var template = require(config.template.lib);

  // Pre compile the template
  // var compiled = template.compile(files.templates.contents[0], {
  //   filename: config.template.path
  // });

  // Pre compile the template(s)
  var compiled = {};

  files.templates.contents.forEach(function () {

  });

  // If the target folder doesn't exist already, create one.
  if (!fs.existsSync(config.target)) {

    fs.mkdirSync(config.target);

  }

  // For each schema that has been have loaded
  files.schemas.contents.forEach(function (file, key) {

    // Generate a html file name from the schema name.
    var htmlName = files.schemas.fileNames[key].split(/\./)[0] + config.suffix;

    // Create a file with the generated name and populate by passing the
    // schema contents through the previously compiled template.
    fs.writeFileSync(config.target + htmlName, compiled(file));

  });

};<commit_msg>Build from multiple mapped templates<commit_after>/*

    build.js

    Accepts:  config  - object, the config data
              files   - object, the files object generated in schema.js

    Creates a set of HTML files based on the json schemas and template
    files within the files object

*/

var fs  = require('fs');

module.exports = function (config, files) {

  // Import the template language
  var template = require(config.template.lib);

  // Pre compile the template
  // var compiled = template.compile(files.templates.contents[0], {
  //   filename: config.template.path
  // });

  // Pre compile the template(s)
  var compiled = {};

  files.templates.contents.forEach(function (file, key) {
    // Create an object key from the file name
    var fileName = files.templates.fileNames[key].split(/\./)[0];

    // Pre-compile the template against that key
    compiled[fileName] = template.compile(file, {
      filename: config.template.path
    });

  });

  // If the target folder doesn't exist already, create one.
  if (!fs.existsSync(config.target)) {

    fs.mkdirSync(config.target);

  }

  // For each schema that has been have loaded
  files.schemas.contents.forEach(function (file, key) {

    // Generate a html file name from the schema name.
    var htmlName = files.schemas.fileNames[key].split(/\./)[0];

    var fileContent = '';

    if (compiled[htmlName]) {
      fileContent = compiled[htmlName](file);
    } else {
      fileContent = compiled[config.template.file](file);
    }

    // Create a file with the generated name and populate by passing the
    // schema contents through the previously compiled template.
    fs.writeFileSync(config.target + htmlName +  config.suffix, fileContent);

  });

};<|endoftext|>


<commit_before>'use strict';

var toTasks = require('./to-tasks');
var utils = require('./utils');

module.exports = function(options) {
  return function(app) {
    var build = app.build;
    delete app.build;

    app.define('build', function(argv, cb) {
      var args = parseTasks(argv, this, this.base, this.env);
      this.emit('argv', argv);

      if (this.option('tasks.choose')) {
        return chooseTasks(this, cb);
      }

      if (this.option('tasks.display')) {
        displayTasks(this);
        return cb();
      }

      if (args.unknown.length) {
        return cb(handleError(args.unknown, this));
      }

      utils.async.each(args.tasks, function(tasks, next) {
        if (!tasks) return next();
        var arg = tasks.split(':');

        try {
          // a task was specified
          if (arg.length === 1) {
            build.call(app, taskArray(tasks), next);
            return;
          }

          // a generator and one or more tasks were specified
          var generator = lookupGenerator(arg[0], app);
          if (!generator) {
            return next(handleError(arg[0]), app);
          }

          generator.option(args.options);
          if (!generator.env) {
            generator.env = app.env;
          }

          generator.env.argv = args;
          tasks = taskArray(arg[1]);

          build.call(generator, tasks, next);
        } catch (err) {
          next(err);
        }
      }, cb);
    });

    function parseTasks(argv, app, base, env) {
      var args = toTasks(argv, app, base, env);
      env.argv = args;
      env.argv.raw = argv;
      app.option(args.options);
      app.cli.process(args.commands);
      return args;
    }
  };
};

function handleError(name, app) {
  var arr = utils.arrayify(name);
  var names = arr.map(function(str) {
     return '"' + str.split('.').pop() + '"';
  });
  var res = names.join(', ');
  var msg = 'cannot find task or generator: "' + res + '"';
  return utils.colors.red(utils.errorSymbol) + ' ' + new Error(msg);
}

function taskArray(tasks) {
  if (typeof tasks === 'string') {
    return tasks.split(',');
  }
  return tasks;
}

function lookupGenerator(name, generate) {
  var app = generate.get(name);
  if (app && !app.isGenerate && !app.isAssemble) {
    app = generate.registerPath(app.alias, app.path);
  }
  return app;
}

function chooseTasks(app, cb) {
  app.disable('choose');
  app.disable('tasks.choose');
  app.chooseTasks(function(err, results) {
    if (err) throw err;
    var obj = utils.tableize(results, true);
    var res = '';
    for (var key in obj) {
      var val = obj[key];
      if (Array.isArray(val)) {
        val = val.join(',');
      }
      res += key + ':' + val;
    }
    app.build(res, cb);
  });
}

function displayTasks(app) {
  app.disable('tasks.display');
  app.displayTasks();
}
<commit_msg>call `app.cli.process` in `build`<commit_after>'use strict';

var toTasks = require('./to-tasks');
var utils = require('./utils');

module.exports = function(options) {
  return function(app) {
    var build = app.build;
    delete app.build;

    app.define('build', function(argv, cb) {
      var args = parseTasks(argv, this, this.base, this.env);
      this.emit('argv', argv);
      app.cli.process(args.commands);

      if (this.option('tasks.choose')) {
        return chooseTasks(this, cb);
      }

      if (this.option('tasks.display')) {
        displayTasks(this);
        return cb();
      }

      if (args.unknown.length) {
        return cb(handleError(args.unknown, this));
      }

      utils.async.each(args.tasks, function(tasks, next) {
        if (!tasks) return next();
        var arg = tasks.split(':');

        try {
          // a task was specified
          if (arg.length === 1) {
            build.call(app, taskArray(tasks), next);
            return;
          }

          // a generator and one or more tasks were specified
          var generator = lookupGenerator(arg[0], app);
          if (!generator) {
            return next(handleError(arg[0]), app);
          }

          generator.option(args.options);
          if (!generator.env) {
            generator.env = app.env;
          }

          generator.env.argv = args;
          tasks = taskArray(arg[1]);

          build.call(generator, tasks, next);
        } catch (err) {
          next(err);
        }
      }, cb);
    });

    function parseTasks(argv, app, base, env) {
      var args = toTasks(argv, app, base, env);
      env.argv = args;
      env.argv.raw = argv;
      app.option(args.options);
      app.cli.process(args.commands);
      return args;
    }
  };
};

function handleError(name, app) {
  var arr = utils.arrayify(name);
  var names = arr.map(function(str) {
     return '"' + str.split('.').pop() + '"';
  });
  var res = names.join(', ');
  var msg = 'cannot find task or generator: "' + res + '"';
  return utils.colors.red(utils.errorSymbol) + ' ' + new Error(msg);
}

function taskArray(tasks) {
  if (typeof tasks === 'string') {
    return tasks.split(',');
  }
  return tasks;
}

function lookupGenerator(name, generate) {
  var app = generate.get(name);
  if (app && !app.isGenerate && !app.isAssemble) {
    app = generate.registerPath(app.alias, app.path);
  }
  return app;
}

function chooseTasks(app, cb) {
  app.disable('choose');
  app.disable('tasks.choose');
  app.chooseTasks(function(err, results) {
    if (err) throw err;
    var obj = utils.tableize(results, true);
    var res = '';
    for (var key in obj) {
      var val = obj[key];
      if (Array.isArray(val)) {
        val = val.join(',');
      }
      res += key + ':' + val;
    }
    app.build(res, cb);
  });
}

function displayTasks(app) {
  app.disable('tasks.display');
  app.displayTasks();
}
<|endoftext|>