<commit_before>const
    GH_RESERVED_USER_NAMES = [
      'settings', 'orgs', 'organizations',
      'site', 'blog', 'about', 'explore',
      'styleguide', 'showcases', 'trending',
      'stars', 'dashboard', 'notifications',
      'search', 'developer', 'account'
    ]
  , GH_RESERVED_REPO_NAMES = ['followers', 'following', 'repositories']
  , GH_BRANCH_SEL       = '*[data-master-branch]'
  , GH_BRANCH_BTN_SEL   = '*[data-master-branch] > .js-select-button'
  , GH_404_SEL          = '#parallax_wrapper'
  , GH_PJAX_SEL         = '#js-repo-pjax-container'
  , GH_CONTAINERS       = 'body > .container, .header > .container, .site > .container, .repohead > .container'

function GitHub() {
  if (!window.MutationObserver) return

  // Fix #151 by detecting when page layout is updated.
  // In this case, split-diff page has a wider layout, so need to recompute margin.
  // Note that couldn't do this in response to URL change, since new DOM via pjax might not be ready.
  var observer = new window.MutationObserver(function(mutations) {
    for (var i = 0, len = mutations.length; i < len; i++) {
      var mutation = mutations[i]
      if (~mutation.oldValue.indexOf('split-diff') ||
          ~mutation.target.className.indexOf('split-diff')) {
        return $(document).trigger(EVENT.LAYOUT_CHANGE)
      }
    }
  })

  observer.observe(document.body, {
    attributes: true,
    attributeFilter: ['class'],
    attributeOldValue: true
  })
}

/**
 * Selects a submodule.
 */
GitHub.prototype.selectSubmodule = function(path) {
  window.location.href = path
}

/**
 * Selects a path.
 */
GitHub.prototype.selectPath = function(path, tabSize) {
  var container = $(GH_PJAX_SEL)
    , qs = tabSize ? ('?ts=' + tabSize) : ''

  if (container.length) {
    $.pjax({
      // needs full path for pjax to work with Firefox as per cross-domain-content setting
      url : location.protocol + '//' + location.host + path + qs,
      container : container
    })
  }
  else window.location.href = path + qs // falls back if no container (i.e. GitHub DOM has changed or is not yet available)
}

/**
 * Updates page layout based on visibility status and width of the Octotree sidebar.
 */
GitHub.prototype.updateLayout = function(sidebarVisible, sidebarWidth) {
  var $containers = $(GH_CONTAINERS)
    , spacing = 10
    , autoMarginLeft
    , shouldPushLeft

  if ($containers.length === 4) {
    autoMarginLeft = ($('body').width() - $containers.width()) / 2
    shouldPushLeft = sidebarVisible && (autoMarginLeft <= sidebarWidth + spacing)
    $containers.css('margin-left', shouldPushLeft ? sidebarWidth + spacing : '')
  }

  // falls-back if GitHub DOM has been updated
  else $('html').css('margin-left', sidebarVisible ? sidebarWidth - spacing : '')
}

/**
 * Returns the repository information if user is at a repository URL. Returns `null` otherwise.
 */
GitHub.prototype.getRepoFromPath = function(showInNonCodePage, currentRepo) {
  // 404 page, skip
  if ($(GH_404_SEL).length) return false

  // (username)/(reponame)[/(subpart)]
  var match = window.location.pathname.match(/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/)
  if (!match) return false

  // not a repository, skip
  if (~GH_RESERVED_USER_NAMES.indexOf(match[1])) return false
  if (~GH_RESERVED_REPO_NAMES.indexOf(match[2])) return false

  // skip non-code page or not
  if (!showInNonCodePage && match[3] && !~['tree', 'blob'].indexOf(match[3])) return false

  // use selected branch, or previously selected branch, or master
  var branch = $(GH_BRANCH_SEL).data('ref') ||
    ((currentRepo.username === match[1] && currentRepo.reponame === match[2] && currentRepo.branch)
      ? currentRepo.branch
      : 'master')

  return {
    username : match[1],
    reponame : match[2],
    branch   : branch
  }
}

/**
 * Fetches data of a particular repository.
 * @param opts: { repo: repository, token (optional): user access token, apiUrl (optional): base API URL }
 * @param cb(err: error, tree: array (of arrays) of items)
 */
GitHub.prototype.fetchData = function(opts, cb) {
  var self = this
    , repo = opts.repo
    , folders = { '': [] }
    , encodedBranch = encodeURIComponent(decodeURIComponent(repo.branch))
    , $dummyDiv = $('
')

  getTree(encodedBranch + '?recursive=true', function(err, tree) {
    if (err) return cb(err)

    fetchSubmodules(function(err, submodules) {
      if (err) return cb(err)
      submodules = submodules || {}

      // split work in chunks to prevent blocking UI on large repos
      nextChunk(0)
      function nextChunk(iteration) {
        var chunkSize = 300
          , baseIndex = iteration * chunkSize
          , i
          , item, path, type, index, name, moduleUrl

        for (i = 0; i < chunkSize; i++) {
          item = tree[baseIndex + i]

          // we're done
          if (item === undefined) return cb(null, folders[''])

          path  = item.path
          type  = item.type
          index = path.lastIndexOf('/')
          name  = $dummyDiv.text(path.substring(index + 1)).html() // sanitizes, closes #9

          item.id   = PREFIX + path
          item.text = name
          item.icon = type // use `type` as class name for tree node

          folders[path.substring(0, index)].push(item)

          if (type === 'tree') {
            folders[item.path] = item.children = []
            item.a_attr = { href: '#' }
          }
          else if (type === 'blob') {
            item.a_attr = { href: '/' + repo.username + '/' + repo.reponame + '/' + type + '/' + repo.branch + '/' + encodeURIComponent(path) /* closes #97 */ }
          }
          else if (type === 'commit') {
            moduleUrl = submodules[item.path]
            if (moduleUrl) { // fix #105
              // special handling for submodules hosted in GitHub
              if (~moduleUrl.indexOf('github.com')) {
                moduleUrl = moduleUrl.replace(/^git:/, window.location.protocol)
                                     .replace(/.git$/, '')
                item.text = '' + name + '' +
                            '@ ' +
                            '' + item.sha.substr(0, 7) + ''
              }
              item.a_attr = { href: moduleUrl }
            }
          }
        }

        setTimeout(function() {
          nextChunk(iteration + 1)
        }, 0)
      }
    })

    function fetchSubmodules(cb) {
      var item = tree.filter(function(item) { return /^\.gitmodules$/i.test(item.path) })[0]
      if (!item) return cb()

      getBlob(item.sha, function(err, data) {
        if (err || !data) return cb(err)
        parseGitmodules(data, cb)
      })
    }
  })

  function getTree(tree, cb) {
   get('/git/trees/' + tree, function(err, res) {
      if (err) return cb(err)
      cb(null, res.tree)
    })
  }

  function getBlob(sha, cb) {
    get('/git/blobs/' + sha, function(err, res) {
      if (err) return cb(err)
      cb(null, atob(res.content.replace(/\n/g,'')))
    })
  }

  function get(path, cb) {
    var token = opts.token
      , host  = (location.host === 'github.com' ? 'api.github.com' : (location.host + '/api/v3'))
      , base  = location.protocol + '//' + host + '/repos/' + repo.username + '/' + repo.reponame
      , cfg   = { method: 'GET', url: base + path, cache: false }

    if (token) cfg.headers = { Authorization: 'token ' + token }
    $.ajax(cfg)
      .done(function(data) {
        cb(null, data)
      })
      .fail(function(jqXHR) {
        var createTokenUrl = location.protocol + '//' + location.host + '/settings/tokens/new'
          , error
          , message
          , needAuth

        switch (jqXHR.status) {
          case 0:
            error = 'Connection error'
            message = 'Cannot connect to GitHub. If your network connection to GitHub is fine, maybe there is an outage of the GitHub API. Please try again later.'
            needAuth = false
            break
          case 401:
            error = 'Invalid token'
            message = 'The token is invalid. Follow this link to create a new token and paste it below.'
            needAuth = true
            break
          case 409:
            error = 'Empty repository'
            message = 'This repository is empty.'
            break
          case 404:
            error = 'Private repository'
            message = 'Accessing private repositories requires a GitHub access token. Follow this link to create one and paste it below.'
            needAuth = true
            break
          case 403:
            if (~jqXHR.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
              error = 'API limit exceeded'
              message = 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow this link to create one and paste it below.'
              needAuth = true
              break
            }
            else {
              error = 'Forbidden'
              message = 'You are not allowed to access the API. You might need to provide an access token. Follow this link to create one and paste it below.'
              needAuth = true
              break
            }
          default:
            error = message = jqXHR.statusText
            needAuth = false
            break
        }
        cb({
          error    : 'Error: ' + error,
          message  : message,
          needAuth : needAuth,
        })
      })
  }
}<commit_msg>Allow navigating to commit trees<commit_after>const
    GH_RESERVED_USER_NAMES = [
      'settings', 'orgs', 'organizations',
      'site', 'blog', 'about', 'explore',
      'styleguide', 'showcases', 'trending',
      'stars', 'dashboard', 'notifications',
      'search', 'developer', 'account'
    ]
  , GH_RESERVED_REPO_NAMES = ['followers', 'following', 'repositories']
  , GH_BRANCH_SEL       = '*[data-master-branch]'
  , GH_BRANCH_BTN_SEL   = '*[data-master-branch] > .js-select-button'
  , GH_404_SEL          = '#parallax_wrapper'
  , GH_PJAX_SEL         = '#js-repo-pjax-container'
  , GH_CONTAINERS       = 'body > .container, .header > .container, .site > .container, .repohead > .container'

function GitHub() {
  if (!window.MutationObserver) return

  // Fix #151 by detecting when page layout is updated.
  // In this case, split-diff page has a wider layout, so need to recompute margin.
  // Note that couldn't do this in response to URL change, since new DOM via pjax might not be ready.
  var observer = new window.MutationObserver(function(mutations) {
    for (var i = 0, len = mutations.length; i < len; i++) {
      var mutation = mutations[i]
      if (~mutation.oldValue.indexOf('split-diff') ||
          ~mutation.target.className.indexOf('split-diff')) {
        return $(document).trigger(EVENT.LAYOUT_CHANGE)
      }
    }
  })

  observer.observe(document.body, {
    attributes: true,
    attributeFilter: ['class'],
    attributeOldValue: true
  })
}

/**
 * Selects a submodule.
 */
GitHub.prototype.selectSubmodule = function(path) {
  window.location.href = path
}

/**
 * Selects a path.
 */
GitHub.prototype.selectPath = function(path, tabSize) {
  var container = $(GH_PJAX_SEL)
    , qs = tabSize ? ('?ts=' + tabSize) : ''

  if (container.length) {
    $.pjax({
      // needs full path for pjax to work with Firefox as per cross-domain-content setting
      url : location.protocol + '//' + location.host + path + qs,
      container : container
    })
  }
  else window.location.href = path + qs // falls back if no container (i.e. GitHub DOM has changed or is not yet available)
}

/**
 * Updates page layout based on visibility status and width of the Octotree sidebar.
 */
GitHub.prototype.updateLayout = function(sidebarVisible, sidebarWidth) {
  var $containers = $(GH_CONTAINERS)
    , spacing = 10
    , autoMarginLeft
    , shouldPushLeft

  if ($containers.length === 4) {
    autoMarginLeft = ($('body').width() - $containers.width()) / 2
    shouldPushLeft = sidebarVisible && (autoMarginLeft <= sidebarWidth + spacing)
    $containers.css('margin-left', shouldPushLeft ? sidebarWidth + spacing : '')
  }

  // falls-back if GitHub DOM has been updated
  else $('html').css('margin-left', sidebarVisible ? sidebarWidth - spacing : '')
}

/**
 * Returns the repository information if user is at a repository URL. Returns `null` otherwise.
 */
GitHub.prototype.getRepoFromPath = function(showInNonCodePage, currentRepo) {
  // 404 page, skip
  if ($(GH_404_SEL).length) return false

  // (username)/(reponame)[/(subpart)]
  var match = window.location.pathname.match(/([^\/]+)\/([^\/]+)(?:\/([^\/]+))?/)
  if (!match) return false

  // not a repository, skip
  if (~GH_RESERVED_USER_NAMES.indexOf(match[1])) return false
  if (~GH_RESERVED_REPO_NAMES.indexOf(match[2])) return false

  // skip non-code page or not
  if (!showInNonCodePage && match[3] && !~['tree', 'blob'].indexOf(match[3])) return false

  // use selected branch, or previously selected branch, or master
  var branch = $(GH_BRANCH_SEL).data('ref') || $(GH_BRANCH_BTN_SEL).text() ||
    ((currentRepo.username === match[1] && currentRepo.reponame === match[2] && currentRepo.branch)
      ? currentRepo.branch
      : 'master')

  return {
    username : match[1],
    reponame : match[2],
    branch   : branch
  }
}

/**
 * Fetches data of a particular repository.
 * @param opts: { repo: repository, token (optional): user access token, apiUrl (optional): base API URL }
 * @param cb(err: error, tree: array (of arrays) of items)
 */
GitHub.prototype.fetchData = function(opts, cb) {
  var self = this
    , repo = opts.repo
    , folders = { '': [] }
    , encodedBranch = encodeURIComponent(decodeURIComponent(repo.branch))
    , $dummyDiv = $('
')

  getTree(encodedBranch + '?recursive=true', function(err, tree) {
    if (err) return cb(err)

    fetchSubmodules(function(err, submodules) {
      if (err) return cb(err)
      submodules = submodules || {}

      // split work in chunks to prevent blocking UI on large repos
      nextChunk(0)
      function nextChunk(iteration) {
        var chunkSize = 300
          , baseIndex = iteration * chunkSize
          , i
          , item, path, type, index, name, moduleUrl

        for (i = 0; i < chunkSize; i++) {
          item = tree[baseIndex + i]

          // we're done
          if (item === undefined) return cb(null, folders[''])

          path  = item.path
          type  = item.type
          index = path.lastIndexOf('/')
          name  = $dummyDiv.text(path.substring(index + 1)).html() // sanitizes, closes #9

          item.id   = PREFIX + path
          item.text = name
          item.icon = type // use `type` as class name for tree node

          folders[path.substring(0, index)].push(item)

          if (type === 'tree') {
            folders[item.path] = item.children = []
            item.a_attr = { href: '#' }
          }
          else if (type === 'blob') {
            item.a_attr = { href: '/' + repo.username + '/' + repo.reponame + '/' + type + '/' + repo.branch + '/' + encodeURIComponent(path) /* closes #97 */ }
          }
          else if (type === 'commit') {
            moduleUrl = submodules[item.path]
            if (moduleUrl) { // fix #105
              // special handling for submodules hosted in GitHub
              if (~moduleUrl.indexOf('github.com')) {
                moduleUrl = moduleUrl.replace(/^git:/, window.location.protocol)
                                     .replace(/.git$/, '')
                item.text = '' + name + '' +
                            '@ ' +
                            '' + item.sha.substr(0, 7) + ''
              }
              item.a_attr = { href: moduleUrl }
            }
          }
        }

        setTimeout(function() {
          nextChunk(iteration + 1)
        }, 0)
      }
    })

    function fetchSubmodules(cb) {
      var item = tree.filter(function(item) { return /^\.gitmodules$/i.test(item.path) })[0]
      if (!item) return cb()

      getBlob(item.sha, function(err, data) {
        if (err || !data) return cb(err)
        parseGitmodules(data, cb)
      })
    }
  })

  function getTree(tree, cb) {
   get('/git/trees/' + tree, function(err, res) {
      if (err) return cb(err)
      cb(null, res.tree)
    })
  }

  function getBlob(sha, cb) {
    get('/git/blobs/' + sha, function(err, res) {
      if (err) return cb(err)
      cb(null, atob(res.content.replace(/\n/g,'')))
    })
  }

  function get(path, cb) {
    var token = opts.token
      , host  = (location.host === 'github.com' ? 'api.github.com' : (location.host + '/api/v3'))
      , base  = location.protocol + '//' + host + '/repos/' + repo.username + '/' + repo.reponame
      , cfg   = { method: 'GET', url: base + path, cache: false }

    if (token) cfg.headers = { Authorization: 'token ' + token }
    $.ajax(cfg)
      .done(function(data) {
        cb(null, data)
      })
      .fail(function(jqXHR) {
        var createTokenUrl = location.protocol + '//' + location.host + '/settings/tokens/new'
          , error
          , message
          , needAuth

        switch (jqXHR.status) {
          case 0:
            error = 'Connection error'
            message = 'Cannot connect to GitHub. If your network connection to GitHub is fine, maybe there is an outage of the GitHub API. Please try again later.'
            needAuth = false
            break
          case 401:
            error = 'Invalid token'
            message = 'The token is invalid. Follow this link to create a new token and paste it below.'
            needAuth = true
            break
          case 409:
            error = 'Empty repository'
            message = 'This repository is empty.'
            break
          case 404:
            error = 'Private repository'
            message = 'Accessing private repositories requires a GitHub access token. Follow this link to create one and paste it below.'
            needAuth = true
            break
          case 403:
            if (~jqXHR.getAllResponseHeaders().indexOf('X-RateLimit-Remaining: 0')) {
              error = 'API limit exceeded'
              message = 'You have exceeded the GitHub API hourly limit and need GitHub access token to make extra requests. Follow this link to create one and paste it below.'
              needAuth = true
              break
            }
            else {
              error = 'Forbidden'
              message = 'You are not allowed to access the API. You might need to provide an access token. Follow this link to create one and paste it below.'
              needAuth = true
              break
            }
          default:
            error = message = jqXHR.statusText
            needAuth = false
            break
        }
        cb({
          error    : 'Error: ' + error,
          message  : message,
          needAuth : needAuth,
        })
      })
  }
}
<|endoftext|>


<commit_before>var CONSTANTS = require('../constants.json');
var utils = require('../utils.js');
var bidfactory = require('../bidfactory.js');
var bidmanager = require('../bidmanager.js');
var adloader = require('../adloader');

var allPlacementCodes;

/**
 * Adapter for requesting bids from Sovrn
 */
var SovrnAdapter = function SovrnAdapter() {
  var sovrnUrl = 'ap.lijit.com/rtb/bid';

  function _callBids(params) {
    var sovrnBids = params.bids || [];

    // De-dupe by tagid then issue single bid request for all bids
    _requestBids(_getUniqueTagids(sovrnBids));
  }

  // filter bids to de-dupe them?
  function _getUniqueTagids(bids) {
    var key;
    var map = {};
    var Tagids = [];
    bids.forEach(function (bid) {
      map[utils.getBidIdParamater('tagid', bid.params)] = bid;
    });

    for (key in map) {
      if (map.hasOwnProperty(key)) {
        Tagids.push(map[key]);
      }
    }

    return Tagids;
  }

  function _requestBids(bidReqs) {
    // build bid request object
    var domain = window.location.host;
    var page = window.location.pathname + location.search + location.hash;

    var sovrnImps = [];
    allPlacementCodes = [];

    //build impression array for sovrn
    utils._each(bidReqs, function (bid) {
      var tagId = utils.getBidIdParamater('tagid', bid.params);
      var bidFloor = utils.getBidIdParamater('bidfloor', bid.params);
      var adW = 0;
      var adH = 0;

      //sovrn supports only one size per tagid, so we just take the first size if there are more
      //if we are a 2 item array of 2 numbers, we must be a SingleSize array
      var sizeArrayLength = bid.sizes.length;
      if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') {
        adW = bid.sizes[0];
        adH = bid.sizes[1];
      } else {
        adW = bid.sizes[0][0];
        adH = bid.sizes[0][1];
      }

      var imp =
      {
        id: utils.getUniqueIdentifierStr(),
        banner: {
          w: adW,
          h: adH
        },
        tagid: tagId,
        bidfloor: bidFloor
      };
      sovrnImps.push(imp);
      bidmanager.pbCallbackMap[imp.id] = bid;
      allPlacementCodes.push(bid.placementCode);
    });

    // build bid request with impressions
    var sovrnBidReq = {
      id: utils.getUniqueIdentifierStr(),
      imp: sovrnImps,
      site: {
        domain: domain,
        page: page
      }
    };

    var scriptUrl = '//' + sovrnUrl + '?callback=window.pbjs.sovrnResponse' +
      '&br=' + encodeURIComponent(JSON.stringify(sovrnBidReq));
    adloader.loadScript(scriptUrl, null);
  }

  function addBlankBidResponsesForAllPlacementsExceptThese(placementsWithBidsBack) {
    utils._each(allPlacementCodes, function (placementCode) {
      if (utils.contains(placementsWithBidsBack, placementCode)) {
        // A bid was returned for this placement already
        return null;
      } else {
        // Add a no-bid response for this placement.
        var bid = {};
        bid = bidfactory.createBid(2);
        bid.bidderCode = 'sovrn';
        bidmanager.addBidResponse(placementCode, bid);
      }
    });
  }

  //expose the callback to the global object:
  pbjs.sovrnResponse = function (sovrnResponseObj) {
    // valid object?
    if (sovrnResponseObj && sovrnResponseObj.id) {
      // valid object w/ bid responses?
      if (sovrnResponseObj.seatbid && sovrnResponseObj.seatbid.length !== 0 && sovrnResponseObj.seatbid[0].bid && sovrnResponseObj.seatbid[0].bid.length !== 0) {
        var placementsWithBidsBack = [];
        sovrnResponseObj.seatbid[0].bid.forEach(function (sovrnBid) {

          var responseCPM;
          var placementCode = '';
          var id = sovrnBid.impid;
          var bid = {};

          // try to fetch the bid request we sent Sovrn
          var bidObj = bidmanager.getPlacementIdByCBIdentifer(id);
          if (bidObj) {
            placementCode = bidObj.placementCode;
            placementsWithBidsBack.push(placementCode);
            bidObj.status = CONSTANTS.STATUS.GOOD;

            //place ad response on bidmanager._adResponsesByBidderId
            responseCPM = parseFloat(sovrnBid.price);

            if (responseCPM !== 0) {
              sovrnBid.placementCode = placementCode;
              sovrnBid.size = bidObj.sizes;
              var responseAd = sovrnBid.adm;

              // build impression url from response
              var responseNurl = '';

              //store bid response
              //bid status is good (indicating 1)
              bid = bidfactory.createBid(1);
              bid.creative_id = sovrnBid.Id;
              bid.bidderCode = 'sovrn';
              bid.cpm = responseCPM;

              //set ad content + impression url
              // sovrn returns


<commit_before>module.exports = AkeraRestApi;

var akeraApi = require('akera-api');
var p = akeraApi.call.parameter;
var akeraApp = null;

function AkeraRestApi(akeraWebApp) {
  var self = this;

  this.error = function(err, res) {
    if (err) {
      if (typeof err === 'string') {
        err = {
          message : err
        };
      }

      res.status(500).send(err);
      akeraApp.log('error', err.message);
    }
  };

  this.handleRequest = function(req, res) {
    var broker = req.__broker;
    var callInfo = req.body.call;

    if (!callInfo || !callInfo.procedure) {
      self.error('Invalid or no procedure details specified.', res, akeraApp);
      return;
    }

    try {
      akeraApi.connect(broker).then(function(conn) {
        try {
          var call = conn.call.procedure(callInfo.procedure);

          if (callInfo.parameters instanceof Array) {
            var parameters = [];

            callInfo.parameters.forEach(function(param) {

              param.dataType = param.dataType || 'character';
              param.dataType = param.dataType.toLowerCase();

              switch (param.type) {
              case 'inout':
                parameters.push(p.inout(param.value, param.dataType));
                break;
              case 'output':
                parameters.push(p.output(param.dataType));
                break;
              default:
                parameters.push(p.input(param.value, param.dataType));
              }
            });

            call.parameters.apply(call, parameters);
          }

          call.run().then(function(response) {
            conn.disconnect();
            res.status(200).send(response);
          }, function(err) {
            conn.disconnect();
            self.error(err, res, akeraApp);
          });
        } catch (err) {
          self.error(err, res, akeraApp);
        }
      }, function(err) {
        self.error(err, res, akeraApp);
      });
    } catch (err) {
      self.error(err, res, akeraApp);
    }
  };

  this.init = function(config, router) {

    if (!router || !router.__app || typeof router.__app.require !== 'function')
      throw new Error('Invalid Akera web service router.');

    config = config || {};
    akeraApp = router.__app;
    var restRoute = akeraApp.getRoute(config.route || '/rest/api/');

    router.post(restRoute, self.handleRequest);
    router.get(restRoute, self.handleRequest);

  };

  if (akeraWebApp !== undefined) {
    throw new Error('Rest API service can only be mounted at the broker level.');
  }
}

AkeraRestApi.init = function(config, router) {
  var akeraRestApi = new AkeraRestApi();
  akeraRestApi.init(config, router);
};
<commit_msg>fix lint complaint <commit_after>module.exports = AkeraRestApi;

var akeraApi = require('akera-api');
var p = akeraApi.call.parameter;
var akeraApp = null;

function AkeraRestApi(akeraWebApp) {
  var self = this;

  this.error = function(err, res) {
    if (err) {
      if (typeof err === 'string') {
        err = {
          message : err
        };
      }

      res.status(500).send(err);
      akeraApp.log('error', err.message);
    }
  };

  this.handleRequest = function(req, res) {
    var broker = req.__broker;
    var callInfo = req.body.call;

    if (!callInfo || !callInfo.procedure) {
      self.error('Invalid or no procedure details specified.', res, akeraApp);
      return;
    }

    try {
      akeraApi.connect(broker).then(function(conn) {
        try {
          var call = conn.call.procedure(callInfo.procedure);

          if (callInfo.parameters instanceof Array) {
            var parameters = [];

            callInfo.parameters.forEach(function(param) {

              param.dataType = param.dataType || 'character';
              param.dataType = param.dataType.toLowerCase();

              switch (param.type) {
              case 'inout':
                parameters.push(p.inout(param.value, param.dataType));
                break;
              case 'output':
                parameters.push(p.output(param.dataType));
                break;
              default:
                parameters.push(p.input(param.value, param.dataType));
              }
            });

            call.parameters.apply(call, parameters);
          }

          call.run().then(function(response) {
            conn.disconnect();
            res.status(200).send(response);
          }, function(err) {
            conn.disconnect();
            self.error(err, res, akeraApp);
          });
        } catch (err) {
          self.error(err, res, akeraApp);
        }
      }, function(err) {
        self.error(err, res, akeraApp);
      });
    } catch (err) {
      self.error(err, res, akeraApp);
    }
  };

  this.init = function(config, router) {

    if (!router || !router.__app || typeof router.__app.require !== 'function') {
      throw new Error('Invalid Akera web service router.');
    }

    config = config || {};
    akeraApp = router.__app;
    var restRoute = akeraApp.getRoute(config.route || '/rest/api/');

    router.post(restRoute, self.handleRequest);
    router.get(restRoute, self.handleRequest);

  };

  if (akeraWebApp !== undefined) {
    throw new Error('Rest API service can only be mounted at the broker level.');
  }
}

AkeraRestApi.init = function(config, router) {
  var akeraRestApi = new AkeraRestApi();
  akeraRestApi.init(config, router);
};
<|endoftext|>


<commit_before>"use strict";

var mongoose = require("mongoose");
var Promise = require("bluebird");
var constants = require("./constants");

var Schema = mongoose.Schema;
var INITIAL = constants.INITIAL;
var PENDING= constants.PENDING;
var DONE = constants.DONE;
var SAVE = constants.SAVE;
var UPDATE = constants.UPDATE;
var REMOVE = constants.REMOVE;
var utils;
var updateState;
var modelCache;
var setModel;
var getModel;
var TaskMdl;
var Roller;

var TaskProvider = function (mongoose, _TaskMdl) {
  TaskMdl = _TaskMdl;
  Roller = require("./roller")(mongoose, _TaskMdl);
  utils = require("./utils")(mongoose);
  updateState = utils.updateState;
  modelCache = utils.modelCache;
  setModel = utils.setModel;
  getModel = utils.getModel;

  return Task;
};

var Task = function(){
  var task = this;
  var index = 0;
  var steps = [];

  task.getTaskCollection = function(){
    return TaskMdl;
  };

  task.initModel = function(modelName, schema){
    // if(!(modelName && schema)) throw new Error("Missing Args: modelName, Schema, or both");
    if(modelCache[modelName]) throw new Error("The schema for this model has already been set");
    if (schema && !isObject(schema)) throw new Error("Invalid Schema");

    var DEFAULT_SCHEMA = new Schema({}, {strict: false});

    setModel(modelName, schema ? new Schema(schema, {strict: true}) : DEFAULT_SCHEMA);

    return task;
  };

  task.update = function(model, condition, data){
    if(!data){
      if(!validDoc(model)) throw new Error("Invalid doc");

      data = condition;
      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid model");
    if(!isObject(condition)) throw new Error("Invalid Condition");
    if(!isObject(data)) throw new Error("Invalid data");

    handle$Token(condition);
    handle$Token(data);

    var updateObj = {
      index: index
      , type: UPDATE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
      , data: data
    };

    steps.push(updateObj);
    index++;

    return task;
  };

  task.save = function(model, doc){
    if(!doc){
      if(!validDoc(model)) throw new Error("Invalid doc");

      doc = model.toObject();
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(doc)) throw new Error("Invalid doc");

    var saveObj = {
      index: index
      , type: SAVE
      , state: INITIAL
      , name: getModelName(model)
      , data: doc
    };

    steps.push(saveObj);
    index++;

    return task;
  };

  task.remove = function(model, condition){
    if(!condition){
      if(!validDoc(model)) throw new Error("Invalid doc");

      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(condition)) throw new Error("Invalid Condition");

    handle$Token(condition);

    var removeObj = {
      index: index
      , type: REMOVE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
    };

    steps.push(removeObj);
    index++;

    return task;
  };

  task.options = function(options){
    if(!steps.length) throw new Error("Can't set options on non-existing task");
    if(!isObject(options)) throw new Error("Invalid Options");

    var obj = steps[steps.length - 1];

    if(obj.type !== UPDATE){
      throw new Error("the " + obj.type + " function does not accept options");
    }

    obj.options = options;
    return task
  };

  task.run = function(){
    var chain = Promise.resolve();
    var dbTask = new TaskMdl({steps: steps});
    steps = [];
    index = 0;

    return dbTask.save().then(function (_task) {
      _task.steps.forEach(function (step) {
        chain = chain.then(function () {
          return getResolveFunc(step)(step, _task);
        });
      });

      return chain.then(function () {
        return _task.remove();
      }).catch(function (err) {
        return Roller.rollOne(_task).then(function () {
          throw err;
        });
      });
    });
  };
};

function getResolveFunc(step){
  switch(step.type){
    case UPDATE: return performUpdate;
    case SAVE: return performSave;
    case REMOVE: return performRemove;
  }
}

function performUpdate(update, task) {
  var Collection = getModel(update.name);

  handle$Token(update.condition, true);
  handle$Token(update.data, true);

  // console.log(update);
  return storeOldData(update, task)
    .then(function(){
      return updateState(task, update.index, PENDING)
        .then(function(_task){
          return Collection.update(update.condition, update.data, update.options)
            .exec()
            .then(function(){
              return updateState(_task, update.index, DONE);
            });
        });
    });
}

//todo: store id of saved doc
function performSave(save, task){
  var Collection = getModel(save.name);
  var doc = new Collection(save.data);
  var dataStore = [];

  doc._id = doc._id || utils.generateId();
  dataStore.push({_id: doc._id});

  task.steps[save.index].dataStore = dataStore;
  task.steps[save.index].markModified("dataStore");

  return updateState(task, save.index, PENDING).then(function (_task) {
    return doc.save().then(function () {
      return updateState(_task, save.index, DONE);
    });
  });
}

function performRemove(remove, task){
  var Collection = getModel(remove.name);

  handle$Token(remove.condition, true);

  return storeOldData(remove, task).then(function () {
    return updateState(task, remove.index, PENDING).then(function (_task) {
      return Collection.remove(remove.condition).exec().then(function () {
        return updateState(_task, remove.index, DONE);
      });
    });
  });
}

function storeOldData(step, task){
  var Collection = getModel(step.name);
  var options = step.options || step.type === REMOVE ? {multi: true} : null;
  var query = Collection.find(step.condition);
  var searchQuery = options && options.multi === true
    ? query
    : query.limit(1);

  return searchQuery
    .exec()
    .then(function(result) {
      var oldData = [];

      for (var i = 0; i < result.length; i++) {
        oldData.push(result[i].toObject());
      }

      task.steps[step.index].dataStore = oldData;
      task.steps[step.index].markModified("dataStore");
    });
}

function validModel(model){
  if(typeof model === "string") return true;

  return typeof model === "function" && model.modelName;
}

function validDoc(doc){
  return doc && doc._doc && doc.constructor && doc.constructor.modelName
}

function isObject(obj){
  return obj && typeof obj === "object";
}

function getModelName(model){
  return typeof model === "string" ? model : model.modelName;
}

function handle$Token(obj, fix){
  var ESCAPE_PREFIX = constants.ESCAPE_PREFIX;
  var escapeLength = ESCAPE_PREFIX.length;
  var keys = Object.keys(obj);
  var key;
  var $key;

  for(var i = 0; i < keys.length; i++){
    key = keys[i];

    if(!fix && key[0] === "$"){
      obj[ESCAPE_PREFIX + key] = obj[key];
      delete obj[key];
    }

    if (!fix && typeof obj[key] === 'object') {
      var childKeys = Object.keys(obj[key]);
      for(var j = 0; j < childKeys.length; j++){
        var current = childKeys[j];
        if(current[0] === "$"){
          obj[key][ESCAPE_PREFIX + current] = obj[key][current];
          delete obj[key][current];
        }
      }
    }

    if(fix && key.length >= escapeLength && key.substring(0, escapeLength) === ESCAPE_PREFIX){
      $key = key.replace(ESCAPE_PREFIX, "");
      obj[$key] = obj[key];
      delete obj[key];
    }

    if(fix && typeof obj[key] === 'object'){
      var childKeys = Object.keys(obj[key]);
      for(var j = 0; j < childKeys.length; j++){
        var current = childKeys[j];
        if(current.indexOf(ESCAPE_PREFIX) === 0){
          var $current = current.replace(ESCAPE_PREFIX, "");
          obj[key][$current] = obj[key][current];
          delete obj[key][current];
        }
      }
    }
  }
}

module.exports = TaskProvider;
<commit_msg>Fix task escaping with recursive solution<commit_after>"use strict";

var mongoose = require("mongoose");
var Promise = require("bluebird");
var constants = require("./constants");

var Schema = mongoose.Schema;
var INITIAL = constants.INITIAL;
var PENDING= constants.PENDING;
var DONE = constants.DONE;
var SAVE = constants.SAVE;
var UPDATE = constants.UPDATE;
var REMOVE = constants.REMOVE;
var utils;
var updateState;
var modelCache;
var setModel;
var getModel;
var TaskMdl;
var Roller;

var TaskProvider = function (mongoose, _TaskMdl) {
  TaskMdl = _TaskMdl;
  Roller = require("./roller")(mongoose, _TaskMdl);
  utils = require("./utils")(mongoose);
  updateState = utils.updateState;
  modelCache = utils.modelCache;
  setModel = utils.setModel;
  getModel = utils.getModel;

  return Task;
};

var Task = function(){
  var task = this;
  var index = 0;
  var steps = [];

  task.getTaskCollection = function(){
    return TaskMdl;
  };

  task.initModel = function(modelName, schema){
    // if(!(modelName && schema)) throw new Error("Missing Args: modelName, Schema, or both");
    if(modelCache[modelName]) throw new Error("The schema for this model has already been set");
    if (schema && !isObject(schema)) throw new Error("Invalid Schema");

    var DEFAULT_SCHEMA = new Schema({}, {strict: false});

    setModel(modelName, schema ? new Schema(schema, {strict: true}) : DEFAULT_SCHEMA);

    return task;
  };

  task.update = function(model, condition, data){
    if(!data){
      if(!validDoc(model)) throw new Error("Invalid doc");

      data = condition;
      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid model");
    if(!isObject(condition)) throw new Error("Invalid Condition");
    if(!isObject(data)) throw new Error("Invalid data");

    handle$Token(condition);
    handle$Token(data);

    var updateObj = {
      index: index
      , type: UPDATE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
      , data: data
    };

    steps.push(updateObj);
    index++;

    return task;
  };

  task.save = function(model, doc){
    if(!doc){
      if(!validDoc(model)) throw new Error("Invalid doc");

      doc = model.toObject();
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(doc)) throw new Error("Invalid doc");

    var saveObj = {
      index: index
      , type: SAVE
      , state: INITIAL
      , name: getModelName(model)
      , data: doc
    };

    steps.push(saveObj);
    index++;

    return task;
  };

  task.remove = function(model, condition){
    if(!condition){
      if(!validDoc(model)) throw new Error("Invalid doc");

      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(condition)) throw new Error("Invalid Condition");

    handle$Token(condition);

    var removeObj = {
      index: index
      , type: REMOVE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
    };

    steps.push(removeObj);
    index++;

    return task;
  };

  task.options = function(options){
    if(!steps.length) throw new Error("Can't set options on non-existing task");
    if(!isObject(options)) throw new Error("Invalid Options");

    var obj = steps[steps.length - 1];

    if(obj.type !== UPDATE){
      throw new Error("the " + obj.type + " function does not accept options");
    }

    obj.options = options;
    return task
  };

  task.run = function(){
    var chain = Promise.resolve();
    var dbTask = new TaskMdl({steps: steps});
    steps = [];
    index = 0;

    return dbTask.save().then(function (_task) {
      _task.steps.forEach(function (step) {
        chain = chain.then(function () {
          return getResolveFunc(step)(step, _task);
        });
      });

      return chain.then(function () {
        return _task.remove();
      }).catch(function (err) {
        return Roller.rollOne(_task).then(function () {
          throw err;
        });
      });
    });
  };
};

function getResolveFunc(step){
  switch(step.type){
    case UPDATE: return performUpdate;
    case SAVE: return performSave;
    case REMOVE: return performRemove;
  }
}

function performUpdate(update, task) {
  var Collection = getModel(update.name);

  handle$Token(update.condition, true);
  handle$Token(update.data, true);

  // console.log(update);
  return storeOldData(update, task)
    .then(function(){
      return updateState(task, update.index, PENDING)
        .then(function(_task){
          return Collection.update(update.condition, update.data, update.options)
            .exec()
            .then(function(){
              return updateState(_task, update.index, DONE);
            });
        });
    });
}

//todo: store id of saved doc
function performSave(save, task){
  var Collection = getModel(save.name);
  var doc = new Collection(save.data);
  var dataStore = [];

  doc._id = doc._id || utils.generateId();
  dataStore.push({_id: doc._id});

  task.steps[save.index].dataStore = dataStore;
  task.steps[save.index].markModified("dataStore");

  return updateState(task, save.index, PENDING).then(function (_task) {
    return doc.save().then(function () {
      return updateState(_task, save.index, DONE);
    });
  });
}

function performRemove(remove, task){
  var Collection = getModel(remove.name);

  handle$Token(remove.condition, true);

  return storeOldData(remove, task).then(function () {
    return updateState(task, remove.index, PENDING).then(function (_task) {
      return Collection.remove(remove.condition).exec().then(function () {
        return updateState(_task, remove.index, DONE);
      });
    });
  });
}

function storeOldData(step, task){
  var Collection = getModel(step.name);
  var options = step.options || step.type === REMOVE ? {multi: true} : null;
  var query = Collection.find(step.condition);
  var searchQuery = options && options.multi === true
    ? query
    : query.limit(1);

  return searchQuery
    .exec()
    .then(function(result) {
      var oldData = [];

      for (var i = 0; i < result.length; i++) {
        oldData.push(result[i].toObject());
      }

      task.steps[step.index].dataStore = oldData;
      task.steps[step.index].markModified("dataStore");
    });
}

function validModel(model){
  if(typeof model === "string") return true;

  return typeof model === "function" && model.modelName;
}

function validDoc(doc){
  return doc && doc._doc && doc.constructor && doc.constructor.modelName
}

function isObject(obj){
  return obj === Object(obj);
}

function getModelName(model){
  return typeof model === "string" ? model : model.modelName;
}

function handle$Token(obj, fix){
  var ESCAPE_PREFIX = constants.ESCAPE_PREFIX;
  var escapeLength = ESCAPE_PREFIX.length;
  var keys = Object.keys(obj);
  var key;
  var $key;

  for(var i = 0; i < keys.length; i++){
    key = keys[i];

    if(isObject(obj[key])){
      handle$Token(obj[key], fix);
    }

    if(!fix && key[0] === "$"){
      obj[ESCAPE_PREFIX + key] = obj[key];
      delete obj[key];
    }

    if(fix && key.length >= escapeLength && key.substring(0, escapeLength) === ESCAPE_PREFIX){
      $key = key.replace(ESCAPE_PREFIX, "");
      obj[$key] = obj[key];
      delete obj[key];
    }
  }
}

module.exports = TaskProvider;
<|endoftext|>


<commit_before>var map, tds_wms, animate_layer;
var start_time, end_time;

function initMap() {
  var mapOptions = { maxResolution: 256/512, numZoomLevels: 11, fractionalZoom: true};
  //map = new OpenLayers.Map('map',mapOptions);
  map = new OpenLayers.Map('map');
  map.addControl(new OpenLayers.Control.LayerSwitcher());
  //map.addControl(new OpenLayers.Control.Animate());

  var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
                                        "http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic'} );
  map.addLayer(layer);

  // WMS tiled
  layer = new OpenLayers.Layer.WMS("World Map",
                                   "http://www2.demis.nl/wms/wms.ashx?WMS=WorldMap",
                                   {layers: '*', format: 'image/png'},
                                   {singleTile: false}
                                  );
  layer.setVisibility(true)
  map.addLayer(layer);
  map.zoomToMaxExtent();

  initLayer();
}

function initUI(layer) {
  var max = layer.timesteps.length - 1;

  $("#prev").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('prev button clicked');
    current = $("#slider").slider( "values", 0 );
    if (current > 0 ) {
      $("#slider").slider( "value", current - 1 );
    }
  });
  $("#slider").slider({
    value: 0,
    min: 0,
    max: max,
    step: 1,
    slide: function(e, ui) {
      var step = parseInt(ui.value);
      //console.log("sliding ...");
      $("#time").text(layer.timesteps[step]);
    },
    change: function(e, ui) {
      //tds_wms.setOpacity(ui.value / 10);
      var step = parseInt(ui.value);
      //console.log("step: " + step);
      $("#time").text(layer.timesteps[step]);
      tds_wms.mergeNewParams({'time': layer.timesteps[step]});
    }
  });
  $("#slider-range").slider({
    range: true,
    values: [0, max],
    min: 0,
    max: max,
    step: 1,
    slide: function(e, ui) {
      var step0 = parseInt(ui.values[0]);
      var step1 = parseInt(ui.values[1]);

      console.log("sliding range ...");
      $( "#time-range" ).text( layer.timesteps[step0] + " - " + layer.timesteps[step1] );
      start_time = layer.timesteps[step0];
      end_time = layer.timesteps[step1];
    },
  });
  $("#next").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('next button clicked');
    current = $("#slider").slider( "values", 0 );
    if (current < max ) {
      $("#slider").slider( "value", current + 1 );
    }
  });
  $("#play").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('play button clicked');
    animate(layer);
  });
  $("#stop").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('stop button clicked');
    if (animate_layer != null) {
      map.removeLayer(animate_layer);
    }
  });
}

function initLayer() {
  wps = new SimpleWPS({
    process: "org.malleefowl.wms.layer",
    onSuccess: function(result) {
      console.log(result);
      layer = result[0]
      // wms-t from tds
      tds_wms = new OpenLayers.Layer.WMS(layer.title,
                                         layer.service,
                                         {layers: layer.name,
                                          transparent: true,
                                          format:'image/png',
                                          time: layer.timesteps[0]});

      map.addLayer(tds_wms);

      //map.zoomToExtent(new OpenLayers.Bounds(-100.898437,22.148438,-78.398437,39.726563));

      initUI(layer);
    },
  });
  wps.execute();
}

function animate(layer) {
  wps = new SimpleWPS({
    process: "org.malleefowl.wms.animate",
    raw: false,
    format: 'xml',
    onSuccess: function(xmlDoc) {
      //console.log(xmlDoc);
      var url = $(xmlDoc).find("wps\\:Reference, Reference").first().attr('href');
      console.log(url);

      animate_layer = new OpenLayers.Layer.Image(
        "Animation", url, map.getExtent(), map.getSize(), {
        isBaseLayer: false,
        alwaysInRange: true // Necessary to always draw the image
        });
      map.addLayer(animate_layer);
    },
  });
  wps.execute({
    layer: layer.name,
    start: start_time,
    end: end_time,
    width: map.getSize().w,
    height: map.getSize().h,
    bbox: map.getExtent().toBBOX(),
  });
}

function animateSlow(step) {
  if (step < 10) {
    $("#slider").slider( "value", step );
    setTimeout(function() {animate(step+1);}, 500);
  }
}

<commit_msg>showing all layers in map<commit_after>var map, animate_layer;
var start_time, end_time;

function initMap() {
  var mapOptions = { maxResolution: 256/512, numZoomLevels: 11, fractionalZoom: true};
  //map = new OpenLayers.Map('map',mapOptions);
  map = new OpenLayers.Map('map');
  map.addControl(new OpenLayers.Control.LayerSwitcher());
  //map.addControl(new OpenLayers.Control.Animate());

  var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
                                        "http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic'} );
  map.addLayer(layer);

  // WMS tiled
  layer = new OpenLayers.Layer.WMS("World Map",
                                   "http://www2.demis.nl/wms/wms.ashx?WMS=WorldMap",
                                   {layers: '*', format: 'image/png'},
                                   {singleTile: false}
                                  );
  layer.setVisibility(true)
  map.addLayer(layer);
  map.zoomToMaxExtent();

  initLayer();
}

function initUI(layer) {
  var max = layer.timesteps.length - 1;

  $("#prev").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('prev button clicked');
    current = $("#slider").slider( "values", 0 );
    if (current > 0 ) {
      $("#slider").slider( "value", current - 1 );
    }
  });
  $("#slider").slider({
    value: 0,
    min: 0,
    max: max,
    step: 1,
    slide: function(e, ui) {
      var step = parseInt(ui.value);
      //console.log("sliding ...");
      $("#time").text(layer.timesteps[step]);
    },
    change: function(e, ui) {
      //tds_wms.setOpacity(ui.value / 10);
      var step = parseInt(ui.value);
      //console.log("step: " + step);
      $("#time").text(layer.timesteps[step]);
      layer.wms_layer.mergeNewParams({'time': layer.timesteps[step]});
    }
  });
  $("#slider-range").slider({
    range: true,
    values: [0, max],
    min: 0,
    max: max,
    step: 1,
    slide: function(e, ui) {
      var step0 = parseInt(ui.values[0]);
      var step1 = parseInt(ui.values[1]);

      console.log("sliding range ...");
      $( "#time-range" ).text( layer.timesteps[step0] + " - " + layer.timesteps[step1] );
      start_time = layer.timesteps[step0];
      end_time = layer.timesteps[step1];
    },
  });
  $("#next").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('next button clicked');
    current = $("#slider").slider( "values", 0 );
    if (current < max ) {
      $("#slider").slider( "value", current + 1 );
    }
  });
  $("#play").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('play button clicked');
    animate(layer);
  });
  $("#stop").button({
    icons: {
      primary: "ui-icon-gear",
      secondary: "ui-icon-triangle-1-s"
    },
    text: false,
  }).click(function( event ) {
    console.log('stop button clicked');
    if (animate_layer != null) {
      map.removeLayer(animate_layer);
    }
  });
}

function initLayer() {
  wps = new SimpleWPS({
    process: "org.malleefowl.wms.layer",
    onSuccess: function(result) {
      console.log(result);
      $.each(result, function(index, layer) {
        wms_layer = new OpenLayers.Layer.WMS(layer.title,
                                             layer.service,
                                             {layers: layer.name,
                                              transparent: true,
                                              format:'image/png',
                                              time: layer.timesteps[0]});

        wms_layer.setVisibility(false);
        map.addLayer(wms_layer);
        layer.wms_layer = wms_layer;
        wms_layer.events.register("visibilitychanged", layer, function() {
          if (layer.wms_layer.getVisibility()) {
            initUI(this);
          }
        });
      });
    },
  });
  wps.execute();
}

function animate(layer) {
  wps = new SimpleWPS({
    process: "org.malleefowl.wms.animate",
    raw: false,
    format: 'xml',
    onSuccess: function(xmlDoc) {
      //console.log(xmlDoc);
      var url = $(xmlDoc).find("wps\\:Reference, Reference").first().attr('href');
      console.log(url);

      animate_layer = new OpenLayers.Layer.Image(
        "Animation", url, map.getExtent(), map.getSize(), {
        isBaseLayer: false,
        alwaysInRange: true // Necessary to always draw the image
        });
      map.addLayer(animate_layer);
    },
  });
  wps.execute({
    layer: layer.name,
    start: start_time,
    end: end_time,
    width: map.getSize().w,
    height: map.getSize().h,
    bbox: map.getExtent().toBBOX(),
  });
}

function animateSlow(step) {
  if (step < 10) {
    $("#slider").slider( "value", step );
    setTimeout(function() {animate(step+1);}, 500);
  }
}

<|endoftext|>


<commit_before>'use strict';

import WebSocket from 'ws';
import Promise from 'es6-promises';

let options = {
  url: '',
  headers: {}
};
let ws = null;

function prepareWebSocket(resolve=null, reject=null) {
  ws = new WebSocket(options.url, {
    headers: options.headers
  });
  ws.on('open', () => {
    console.log('WebSocket opened.');
    ws.on('error', e => {
      console.log('WebSocket error.', e);
      prepareWebSocket();
      if (resolve) {
        resolve(e);
      }
    });
    ws.on('close', () => {
      console.log('WebSocket closed.');
      prepareWebSocket();
      if (reject) {
        reject('closed');
      }
    });
    if (resolve) {
      resolve();
    }
  });
}

export function start(url, user, password) {
  if (!url) {
    throw new Error('Missing URL!');
  }
  options.url = url;
  return new Promise((resolve, reject) => {
    if (user && password) {
      let auth = new Buffer(`${user}:${password}`).toString('base64');
      options.headers.Authorization = `Basic ${auth}`;
    }
    prepareWebSocket(resolve, reject);
    ws.on('message', data => {
      if (process.env.WS_DEBUG) {
        console.log('Data:', data);
      }
    });
    console.log('ready');
  });
}

export function send(data) {
  return new Promise((resolve, reject) => {
    if (!data) {
      reject('no data');
      return;
    }
    try {
      ws.send(JSON.stringify(data));
      resolve();
    } catch (e) {
      reject(e);
    }
  });
}<commit_msg>Describe the data direction (send/receive) on the debug log<commit_after>'use strict';

import WebSocket from 'ws';
import Promise from 'es6-promises';

let options = {
  url: '',
  headers: {}
};
let ws = null;

function prepareWebSocket(resolve=null, reject=null) {
  ws = new WebSocket(options.url, {
    headers: options.headers
  });
  ws.on('open', () => {
    console.log('WebSocket opened.');
    ws.on('error', e => {
      console.log('WebSocket error.', e);
      prepareWebSocket();
      if (resolve) {
        resolve(e);
      }
    });
    ws.on('close', () => {
      console.log('WebSocket closed.');
      prepareWebSocket();
      if (reject) {
        reject('closed');
      }
    });
    if (resolve) {
      resolve();
    }
  });
}

export function start(url, user, password) {
  if (!url) {
    throw new Error('Missing URL!');
  }
  options.url = url;
  return new Promise((resolve, reject) => {
    if (user && password) {
      let auth = new Buffer(`${user}:${password}`).toString('base64');
      options.headers.Authorization = `Basic ${auth}`;
    }
    prepareWebSocket(resolve, reject);
    ws.on('message', data => {
      if (process.env.WS_DEBUG) {
        console.log('[RECV]:', data);
      }
    });
    console.log('ready');
  });
}

export function send(data) {
  return new Promise((resolve, reject) => {
    if (!data) {
      reject('no data');
      return;
    }
    try {
      if (process.env.WS_DEBUG) {
        console.log('[SEND]:', JSON.stringify(data));
      }
      ws.send(JSON.stringify(data));
      resolve();
    } catch (e) {
      reject(e);
    }
  });
}<|endoftext|>


<commit_before>// @flow
import {
  handleResult,
  sendRequest,
  validateDate,
  validateDateTime,
} from './util';

export default function cad(): Object {
  return {

    fetch: (options: Object = {}): Promise<any> =>
      new Promise((resolve: (data: Object) => void, reject: (reason: Error) => void): mixed => {
        const optionOverrides = {};
        if (options.hasOwnProperty('date-min')) {
          if (!validateDate(options['date-min']) && !validateDateTime(options['date-min'])) {
            if (options['date-min'] !== 'now') {
              return reject(new Error('date-min is not in a valid format.'));
            }
          }
        }
        if (options.hasOwnProperty('date-max')) {
          if (!validateDate(options['date-max']) && !validateDateTime(options['date-max'])) {
            if (options['date-max'].match(/^[+]\d+$/)) {
              optionOverrides['date-max'] = options['date-max'].replace(/[+]/, '%2B');
            } else if (options['date-max'] !== 'now') {
              return reject(new Error('date-max is not in a valid format.'));
            }
          }
        }
        return sendRequest(
          'ssd-api.jpl.nasa.gov',
          '/cad.api',
          Object.assign({}, options, optionOverrides),
          resolve,
          reject,
          handleResult,
          true
        );
      }),

  };
}
<commit_msg>Validate SBDB Orbit Class Values and Close-approach body values, closes #17<commit_after>// @flow
import {
  handleResult,
  sendRequest,
  validateDate,
  validateDateTime,
} from './util';

export default function cad(): Object {
  function validateClass(className: String): boolean {
    const validClasses = [
      'IEO', 'ATE', 'APO', 'AMO', 'MCA', 'IMB', 'MBA', 'OMB', 'TJN', 'CEN', 'TNO',
      'PAA', 'HYA', 'HYP', 'PAR', 'COM', 'JFC', 'HTC', 'ETc', 'CTc', 'JFc',
    ];
    return validClasses.indexOf(className) === -1;
  }

  function validateBody(body: String): boolean {
    const validBodies = [
      'merc', 'venus', 'earth', 'mars', 'juptr', 'satrn', 'urnus', 'neptn', 'pluto', 'moon',
    ];
    return validBodies.indexOf(body.toLowerCase()) === -1;
  }

  return {

    fetch: (options: Object = {}): Promise<any> =>
      new Promise((resolve: (data: Object) => void, reject: (reason: Error) => void): mixed => {
        const optionOverrides = {};
        if (options.hasOwnProperty('date-min')) {
          if (!validateDate(options['date-min']) && !validateDateTime(options['date-min'])) {
            if (options['date-min'] !== 'now') {
              return reject(new Error('date-min is not in a valid format.'));
            }
          }
        }
        if (options.hasOwnProperty('date-max')) {
          if (!validateDate(options['date-max']) && !validateDateTime(options['date-max'])) {
            if (options['date-max'].match(/^[+]\d+$/)) {
              optionOverrides['date-max'] = options['date-max'].replace(/[+]/, '%2B');
            } else if (options['date-max'] !== 'now') {
              return reject(new Error('date-max is not in a valid format.'));
            }
          }
        }
        if (options.hasOwnProperty('class')) {
          if (!validateClass(options.class)) return reject(new Error('Invalid class value'));
        }
        if (options.hasOwnProperty('body')) {
          if (!validateBody(options.body)) return reject(new Error('Invalid body value'));
        }
        return sendRequest(
          'ssd-api.jpl.nasa.gov',
          '/cad.api',
          Object.assign({}, options, optionOverrides),
          resolve,
          reject,
          handleResult,
          true
        );
      }),

  };
}
<|endoftext|>


<commit_before>import {
    SELECT_ORGANISATION,
    DESELECT_ORGANISATION,
    ADD_ORGANISATION,
    DELETE_ORGANISATION,
    EDIT_ORGANISATION
} from '../constants/ActionTypes.js'

const initalState = [
    {
        name: 'JavaScript 101',
        selected: false,
        id: 1
    }
];

export default function organisations ( state = initalState, action ) {
    switch (action.type) {
        case SELECT_ORGANISATION:
            return  state.map(org => {
                if (org.id === action.id) {
                    return Object.assign({}, org, {selected: true})
                }
                return Object.assign({}, org, {selected: false})
            })

        case DESELECT_ORGANISATION:
            return  state.map(org => {
                if (org.id === action.id) {
                    return Object.assign({}, org, {selected: false})
                }
                return org
            })

        case ADD_ORGANISATION:
            return state.concat([{
                name: action.name,
                selected: false,
                id: state.reduce((maxId, todo) => Math.max(maxId, todo.id), 0) +1
            }])

        case EDIT_ORGANISATION:
            return state.map(org => {
                if (org.id === action.id) {
                    return Object.assign({}, org, {name: action.name})
                }
                return org;
            })

        case DELETE_ORGANISATION:
            return state.filter(org => {
                if (org.id !== action.id) return org;
            })

        default:
            return state;
    }
}
<commit_msg>Minor change<commit_after>import {
    SELECT_ORGANISATION,
    DESELECT_ORGANISATION,
    ADD_ORGANISATION,
    DELETE_ORGANISATION,
    EDIT_ORGANISATION
} from '../constants/ActionTypes.js'

const initalState = [
	{
		name: 'JavaScript 101',
		selected: false,
		id: 1,
	}

];

export default function organisations ( state = initalState, action ) {
    switch (action.type) {
        case SELECT_ORGANISATION:
            return  state.map(org => {
                if (org.id === action.id) {
                    return Object.assign({}, org, {selected: true})
                }
                return Object.assign({}, org, {selected: false})
            })

        case DESELECT_ORGANISATION:
            return  state.map(org => {
                if (org.id === action.id) {
                    return Object.assign({}, org, {selected: false})
                }
                return org
            })

        case ADD_ORGANISATION:
            return state.concat([{
                name: action.name,
                selected: false,
                id: state.reduce((maxId, todo) => Math.max(maxId, todo.id), 0) +1
            }])

        case EDIT_ORGANISATION:
            return state.map(org => {
                if (org.id === action.id) {
                    return Object.assign({}, org, {name: action.name})
                }
                return org;
            })

        case DELETE_ORGANISATION:
            return state.filter(org => {
                if (org.id !== action.id) return org;
            })

        default:
            return state;
    }
}
<|endoftext|>


<commit_before>"use strict";

const extend = require('extend');
const HyperSwitch = require('hyperswitch');
const Template = HyperSwitch.Template;
const utils = require('../lib/utils');
const Title = require('mediawiki-title').Title;

const BACKLINKS_CONTINUE_TOPIC_NAME = 'change-prop.backlinks.continue';
const TRANSCLUDES_CONTINUE_TOPIC_NAME = 'change-prop.transcludes.continue';

function createBackLinksTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                list: 'backlinks',
                bltitle: '{{request.params.title}}',
                blfilterredir: 'nonredirects',
                blcontinue: '{{message.continue}}',
                bllimit: 500
            }
        })),
        getContinueToken: (res) => res.body.continue && res.body.continue.blcontinue,
        continueTopic: BACKLINKS_CONTINUE_TOPIC_NAME,
        resourceChangeTag: 'backlinks',
        extractResults: (res) => res.body.query.backlinks
    };
}

function createImageUsageTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                list: 'imageusage',
                iutitle: '{{request.params.title}}',
                iucontinue: '{{message.continue}}',
                iulimit: 500
                // TODO: decide what do we want to do with redirects
            }
        })),
        getContinueToken: (res) => res.body.continue && res.body.continue.iucontinue,
        continueTopic: TRANSCLUDES_CONTINUE_TOPIC_NAME,
        resourceChangeTag: 'transcludes',
        extractResults: (res) =>  res.body.query.imageusage
    };
}

function createTranscludeInTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                prop: 'transcludedin',
                tiprop: 'title',
                tishow: '!redirect',
                titles: '{{request.params.title}}',
                ticontinue: '{{message.continue}}',
                tilimit: 500
            }
        })),
        getContinueToken: (res) => res.body.continue && res.body.continue.ticontinue,
        continueTopic: TRANSCLUDES_CONTINUE_TOPIC_NAME,
        resourceChangeTag: 'transcludes',
        extractResults: (res) => {
            return res.body.query.pages[Object.keys(res.body.query.pages)[0]].transcludedin;
        }
    };
}

function createWikidataTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'wbgetentities',
                ids: '{{message.page_title}}',
                props: 'sitelinks/urls',
                normalize: true
            }
        })),
        resourceChangeTag: 'wikidata',
        shouldProcess: (res) => { return res && res.body && !!res.body.success; },
        extractResults: (res) => {
            const siteLinks = res.body.entities[Object.keys(res.body.entities)[0]].sitelinks;
            return Object.keys(siteLinks).map((siteId) => {
                const pageURI = siteLinks[siteId].url;
                const match = /^https?:\/\/([^\/]+)\/wiki\/(.+)$/.exec(pageURI);
                return {
                    domain: match[1],
                    title: match[2]
                };
            });
        }
    };
}

class DependencyProcessor {
    constructor(options) {
        this.options = options;
        this.log = options.log || function() { };
        this.siteInfoCache = {};
        this.backLinksRequest = createBackLinksTemplate(options);
        this.imageLinksRequest = createImageUsageTemplate(options);
        this.transcludeInRequest = createTranscludeInTemplate(options);
        this.wikidataRequest = createWikidataTemplate(options);
        this.siteInfoRequest = new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                meta: 'siteinfo',
                siprop: 'general|namespaces|namespacealiases'
            }
        }));
    }

    setup(hyper) {
        return hyper.post({
            uri: '/sys/queue/subscriptions',
            body: {
                backlinks_continue: {
                    topic: BACKLINKS_CONTINUE_TOPIC_NAME,
                    exec: [
                        {
                            method: 'post',
                            uri: '/sys/links/backlinks/{message.original_event.title}',
                            body: '{{globals.message}}'
                        }
                    ]
                },
                transclusions_continue: {
                    topic: TRANSCLUDES_CONTINUE_TOPIC_NAME,
                    exec: [
                        {
                            method: 'post',
                            uri: '/sys/links/transcludes/{message.original_event.page_title}',
                            body: '{{globals.message}}'
                        }
                    ]
                }
            }
        });
    }

    processBackLinks(hyper, req) {
        const context = {
            request: req,
            message: req.body
        };
        const originalEvent = req.body.original_event || req.body;
        return this._getSiteInfo(hyper, req.body)
        .then((siteInfo) => {
            return this._fetchAndProcessBatch(hyper, this.backLinksRequest,
                context, siteInfo, originalEvent);
        });
    }

    processTranscludes(hyper, req) {
        const message = req.body;
        const context = {
            request: req,
            message: message
        };
        const originalEvent = req.body.original_event || req.body;
        return this._getSiteInfo(hyper, message)
        .then((siteInfo) => {
            const title = Title.newFromText(req.params.title, siteInfo);
            // First step - process only file uploads
            if (title.getNamespace().isFile()) {
                return this._fetchAndProcessBatch(hyper, this.imageLinksRequest,
                    context, siteInfo, originalEvent);
            }
            return { status: 200 };
        });
    }

    processWikidata(hyper, req) {
        const context = {
            request: req,
            message: req.body
        };
        return hyper.request(this.wikidataRequest.template.expand(context))
        .then((res) => {
            if (this.wikidataRequest.shouldProcess(res)) {
                const items = this.wikidataRequest.extractResults(res);
                return this._sendResourceChanges(hyper, items, req.body,
                    this.wikidataRequest.resourceChangeTag);
            } else {
                this.log('warn/wikidata_description', {
                    msg: 'Could not extract items',
                    event: context.message
                });
            }
        })
        .thenReturn({ status: 200 });
    }

    _fetchAndProcessBatch(hyper, requestTemplate, context, siteInfo, originalEvent) {
        return hyper.post(requestTemplate.template.expand(context))
        .then((res) => {
            const titles = (requestTemplate.extractResults(res) || []).map((item) => {
                return {
                    title: Title.newFromText(item.title, siteInfo).getPrefixedDBKey(),
                    domain: originalEvent.meta.domain
                };
            });
            if (!titles.length) {
                // the batch is complete or the list of transcluded titles is empty
                return { status: 200 };
            }
            let actions = this._sendResourceChanges(hyper, titles,
                originalEvent, requestTemplate.resourceChangeTag);
            if (res.body.continue) {
                actions = actions.then(() =>
                    this._sendContinueEvent(hyper,
                        requestTemplate.continueTopic,
                        originalEvent,
                        requestTemplate.getContinueToken(res)));
            }
            return actions.thenReturn({ status: 200 });
        });
    }

    _sendContinueEvent(hyper, topic, origEvent, continueToken) {
        return hyper.post({
            uri: '/sys/queue/events',
            body: [{
                meta: {
                    topic: topic,
                    schema_uri: 'continue/1',
                    uri: origEvent.meta.uri,
                    request_id: origEvent.meta.request_id,
                    domain: origEvent.meta.domain,
                    dt: origEvent.meta.dt
                },
                triggered_by: utils.triggeredBy(origEvent),
                original_event: origEvent,
                continue: continueToken
            }]
        });
    }

    _sendResourceChanges(hyper, items, originalEvent, tag) {
        return hyper.post({
            uri: '/sys/queue/events',
            body: items.map((item) => {
                // TODO: need to check whether a wiki is http or https!
                const resourceURI =
                    `https://${item.domain}/wiki/${encodeURIComponent(item.title)}`;
                return {
                    meta: {
                        topic: 'resource_change',
                        schema_uri: 'resource_change/1',
                        uri: resourceURI,
                        request_id: originalEvent.meta.request_id,
                        domain: item.domain,
                        dt: originalEvent.meta.dt
                    },
                    triggered_by: utils.triggeredBy(originalEvent),
                    tags: [ 'change-prop', tag ]
                };
            })
        });
    }

    _getSiteInfo(hyper, message) {
        const domain = message.meta.domain;
        if (!this.siteInfoCache[domain]) {
            this.siteInfoCache[domain] = hyper.post(this.siteInfoRequest.expand({
                message: message
            }))
            .then((res) => {
                return {
                    general: {
                        lang: res.body.query.general.lang,
                        legaltitlechars: res.body.query.general.legaltitlechars,
                        case: res.body.query.general.case
                    },
                    namespaces: res.body.query.namespaces,
                    namespacealiases: res.body.query.namespacealiases
                };
            });
        }
        return this.siteInfoCache[domain];
    }
}

module.exports = (options) => {
    const processor = new DependencyProcessor(options);
    return {
        spec: {
            paths: {
                '/setup': {
                    put: {
                        summary: 'setup the module',
                        operationId: 'setup'
                    }
                },
                '/backlinks/{title}': {
                    post: {
                        summary: 'set up the kafka listener',
                        operationId: 'process_backlinks'
                    }
                },
                '/transcludes/{title}': {
                    post: {
                        summary: 'check if the page is transcluded somewhere and update',
                        operationId: 'process_transcludes'
                    }
                },
                '/wikidata_descriptions': {
                    post: {
                        summary: 'find all pages corresponding to ' +
                            'the wikidata item and update summary',
                        operationId: 'process_wikidata'
                    }
                }
            }
        },
        operations: {
            process_backlinks: processor.processBackLinks.bind(processor),
            process_transcludes: processor.processTranscludes.bind(processor),
            process_wikidata: processor.processWikidata.bind(processor),
            setup: processor.setup.bind(processor)
        },
        resources: [{
            uri: '/sys/links/setup'
        }]
    };
};
<commit_msg>Wikidata: Make sure we have the response we need to process the deps<commit_after>"use strict";

const extend = require('extend');
const HyperSwitch = require('hyperswitch');
const Template = HyperSwitch.Template;
const utils = require('../lib/utils');
const Title = require('mediawiki-title').Title;

const BACKLINKS_CONTINUE_TOPIC_NAME = 'change-prop.backlinks.continue';
const TRANSCLUDES_CONTINUE_TOPIC_NAME = 'change-prop.transcludes.continue';

function createBackLinksTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                list: 'backlinks',
                bltitle: '{{request.params.title}}',
                blfilterredir: 'nonredirects',
                blcontinue: '{{message.continue}}',
                bllimit: 500
            }
        })),
        getContinueToken: (res) => res.body.continue && res.body.continue.blcontinue,
        continueTopic: BACKLINKS_CONTINUE_TOPIC_NAME,
        resourceChangeTag: 'backlinks',
        extractResults: (res) => res.body.query.backlinks
    };
}

function createImageUsageTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                list: 'imageusage',
                iutitle: '{{request.params.title}}',
                iucontinue: '{{message.continue}}',
                iulimit: 500
                // TODO: decide what do we want to do with redirects
            }
        })),
        getContinueToken: (res) => res.body.continue && res.body.continue.iucontinue,
        continueTopic: TRANSCLUDES_CONTINUE_TOPIC_NAME,
        resourceChangeTag: 'transcludes',
        extractResults: (res) =>  res.body.query.imageusage
    };
}

function createTranscludeInTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                prop: 'transcludedin',
                tiprop: 'title',
                tishow: '!redirect',
                titles: '{{request.params.title}}',
                ticontinue: '{{message.continue}}',
                tilimit: 500
            }
        })),
        getContinueToken: (res) => res.body.continue && res.body.continue.ticontinue,
        continueTopic: TRANSCLUDES_CONTINUE_TOPIC_NAME,
        resourceChangeTag: 'transcludes',
        extractResults: (res) => {
            return res.body.query.pages[Object.keys(res.body.query.pages)[0]].transcludedin;
        }
    };
}

function createWikidataTemplate(options) {
    return {
        template: new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'wbgetentities',
                ids: '{{message.page_title}}',
                props: 'sitelinks/urls',
                normalize: true
            }
        })),
        resourceChangeTag: 'wikidata',
        shouldProcess: (res) => {
            if (!(res && res.body && !!res.body.success)) {
                return false;
            }
            const entities = res.body.entities || {};
            if (!Object.keys(entities).length) {
                return false;
            }
            const sitelinks = entities[Object.keys(entities)[0]].sitelinks || {};
            if (!Object.keys(sitelinks).length) {
                return false;
            }
            return true;
        },
        extractResults: (res) => {
            const siteLinks = res.body.entities[Object.keys(res.body.entities)[0]].sitelinks;
            return Object.keys(siteLinks).map((siteId) => {
                const pageURI = siteLinks[siteId].url;
                const match = /^https?:\/\/([^\/]+)\/wiki\/(.+)$/.exec(pageURI);
                return {
                    domain: match[1],
                    title: match[2]
                };
            });
        }
    };
}

class DependencyProcessor {
    constructor(options) {
        this.options = options;
        this.log = options.log || function() { };
        this.siteInfoCache = {};
        this.backLinksRequest = createBackLinksTemplate(options);
        this.imageLinksRequest = createImageUsageTemplate(options);
        this.transcludeInRequest = createTranscludeInTemplate(options);
        this.wikidataRequest = createWikidataTemplate(options);
        this.siteInfoRequest = new Template(extend(true, {}, options.templates.mw_api, {
            method: 'post',
            body: {
                format: 'json',
                action: 'query',
                meta: 'siteinfo',
                siprop: 'general|namespaces|namespacealiases'
            }
        }));
    }

    setup(hyper) {
        return hyper.post({
            uri: '/sys/queue/subscriptions',
            body: {
                backlinks_continue: {
                    topic: BACKLINKS_CONTINUE_TOPIC_NAME,
                    exec: [
                        {
                            method: 'post',
                            uri: '/sys/links/backlinks/{message.original_event.title}',
                            body: '{{globals.message}}'
                        }
                    ]
                },
                transclusions_continue: {
                    topic: TRANSCLUDES_CONTINUE_TOPIC_NAME,
                    exec: [
                        {
                            method: 'post',
                            uri: '/sys/links/transcludes/{message.original_event.page_title}',
                            body: '{{globals.message}}'
                        }
                    ]
                }
            }
        });
    }

    processBackLinks(hyper, req) {
        const context = {
            request: req,
            message: req.body
        };
        const originalEvent = req.body.original_event || req.body;
        return this._getSiteInfo(hyper, req.body)
        .then((siteInfo) => {
            return this._fetchAndProcessBatch(hyper, this.backLinksRequest,
                context, siteInfo, originalEvent);
        });
    }

    processTranscludes(hyper, req) {
        const message = req.body;
        const context = {
            request: req,
            message: message
        };
        const originalEvent = req.body.original_event || req.body;
        return this._getSiteInfo(hyper, message)
        .then((siteInfo) => {
            const title = Title.newFromText(req.params.title, siteInfo);
            // First step - process only file uploads
            if (title.getNamespace().isFile()) {
                return this._fetchAndProcessBatch(hyper, this.imageLinksRequest,
                    context, siteInfo, originalEvent);
            }
            return { status: 200 };
        });
    }

    processWikidata(hyper, req) {
        const context = {
            request: req,
            message: req.body
        };
        return hyper.request(this.wikidataRequest.template.expand(context))
        .then((res) => {
            if (this.wikidataRequest.shouldProcess(res)) {
                const items = this.wikidataRequest.extractResults(res);
                return this._sendResourceChanges(hyper, items, req.body,
                    this.wikidataRequest.resourceChangeTag);
            } else {
                this.log('warn/wikidata_description', {
                    msg: 'Could not extract items',
                    event: context.message
                });
            }
        })
        .thenReturn({ status: 200 });
    }

    _fetchAndProcessBatch(hyper, requestTemplate, context, siteInfo, originalEvent) {
        return hyper.post(requestTemplate.template.expand(context))
        .then((res) => {
            const titles = (requestTemplate.extractResults(res) || []).map((item) => {
                return {
                    title: Title.newFromText(item.title, siteInfo).getPrefixedDBKey(),
                    domain: originalEvent.meta.domain
                };
            });
            if (!titles.length) {
                // the batch is complete or the list of transcluded titles is empty
                return { status: 200 };
            }
            let actions = this._sendResourceChanges(hyper, titles,
                originalEvent, requestTemplate.resourceChangeTag);
            if (res.body.continue) {
                actions = actions.then(() =>
                    this._sendContinueEvent(hyper,
                        requestTemplate.continueTopic,
                        originalEvent,
                        requestTemplate.getContinueToken(res)));
            }
            return actions.thenReturn({ status: 200 });
        });
    }

    _sendContinueEvent(hyper, topic, origEvent, continueToken) {
        return hyper.post({
            uri: '/sys/queue/events',
            body: [{
                meta: {
                    topic: topic,
                    schema_uri: 'continue/1',
                    uri: origEvent.meta.uri,
                    request_id: origEvent.meta.request_id,
                    domain: origEvent.meta.domain,
                    dt: origEvent.meta.dt
                },
                triggered_by: utils.triggeredBy(origEvent),
                original_event: origEvent,
                continue: continueToken
            }]
        });
    }

    _sendResourceChanges(hyper, items, originalEvent, tag) {
        return hyper.post({
            uri: '/sys/queue/events',
            body: items.map((item) => {
                // TODO: need to check whether a wiki is http or https!
                const resourceURI =
                    `https://${item.domain}/wiki/${encodeURIComponent(item.title)}`;
                return {
                    meta: {
                        topic: 'resource_change',
                        schema_uri: 'resource_change/1',
                        uri: resourceURI,
                        request_id: originalEvent.meta.request_id,
                        domain: item.domain,
                        dt: originalEvent.meta.dt
                    },
                    triggered_by: utils.triggeredBy(originalEvent),
                    tags: [ 'change-prop', tag ]
                };
            })
        });
    }

    _getSiteInfo(hyper, message) {
        const domain = message.meta.domain;
        if (!this.siteInfoCache[domain]) {
            this.siteInfoCache[domain] = hyper.post(this.siteInfoRequest.expand({
                message: message
            }))
            .then((res) => {
                return {
                    general: {
                        lang: res.body.query.general.lang,
                        legaltitlechars: res.body.query.general.legaltitlechars,
                        case: res.body.query.general.case
                    },
                    namespaces: res.body.query.namespaces,
                    namespacealiases: res.body.query.namespacealiases
                };
            });
        }
        return this.siteInfoCache[domain];
    }
}

module.exports = (options) => {
    const processor = new DependencyProcessor(options);
    return {
        spec: {
            paths: {
                '/setup': {
                    put: {
                        summary: 'setup the module',
                        operationId: 'setup'
                    }
                },
                '/backlinks/{title}': {
                    post: {
                        summary: 'set up the kafka listener',
                        operationId: 'process_backlinks'
                    }
                },
                '/transcludes/{title}': {
                    post: {
                        summary: 'check if the page is transcluded somewhere and update',
                        operationId: 'process_transcludes'
                    }
                },
                '/wikidata_descriptions': {
                    post: {
                        summary: 'find all pages corresponding to ' +
                            'the wikidata item and update summary',
                        operationId: 'process_wikidata'
                    }
                }
            }
        },
        operations: {
            process_backlinks: processor.processBackLinks.bind(processor),
            process_transcludes: processor.processTranscludes.bind(processor),
            process_wikidata: processor.processWikidata.bind(processor),
            setup: processor.setup.bind(processor)
        },
        resources: [{
            uri: '/sys/links/setup'
        }]
    };
};
<|endoftext|>