<commit_before>'use strict';

const express = require('express');
const router = express.Router();
const BPromise = require('bluebird');
const formidable = require('formidable');
const config = require('config');
const log = require('server/util/logapp.js');
const string = require('common/string.js');
const normalizequery = require('server/middleware/normalizequery.js');
const aggregate = require('server/odk/aggregateapi.js');
const transform = require('server/odk/collect/collecttransform.js');
const aggregatesubmission = require('server/odk/aggregatesubmission.js');
const collect = require('server/odk/collect/collectsubmission.js');

const ODK_MASK_URL = config.server.PUBLIC_URL + '/odk';
log.info(`Backing up to ODK Aggregate -- ${config.odk.URL}`);
log.info(`Masking ODK Aggregate backup server with url ${ODK_MASK_URL}`);

const lowerCaseQueryKeys = normalizequery.lowerCaseQueryKeys();

const prepareXMLResponse = (res, statusCode, body) => {
  res.set({'Content-Type': 'text/xml', 'Content-Length': body.length});
  res.status(statusCode);
};

const handleHeadRequest = (req, res) => res.status(401).send('');

/**
 * To mimic the ODK Aggregate server this web server masks, the XML responses
 * with URLs for that original server must be altered to report this app's URL
 * in place of the original ODK Aggregate URL. This ensures that all requests
 * from the client arrive to this application first.
 *
 * @param  {string} xml      Original XML from Aggregate
 * @param  {string} fromHost URL to hide
 * @param  {string} toHost   URL to display
 * @return {string}          Altered XML
 */
const swapHostnames = (xml, fromHost, toHost) => {
  return new BPromise(resolve => {
    resolve(
      xml.replace(new RegExp(string.escapeRegExp(fromHost), 'g'), toHost)
      .replace(new RegExp('\/odk\:[0-9]{2,5}\/', 'g'), '/odk/')
    );
  });
};

router.all('*', aggregate.setOpenRosaHeaders);

router.head('/formList', handleHeadRequest);

router.get('/formList', lowerCaseQueryKeys, (req, res) => {
  let formId = req.query.formid;
  let verbose = req.query.verbose;
  let allVersions = req.query.listallversions;

  return aggregate.formList({formId, verbose, allVersions})
  .spread((odkRes, body) => {
    return BPromise.join(
      odkRes,
      swapHostnames(body, config.odk.URL, ODK_MASK_URL)
    );
  })
  .spread((odkRes, body) => {
    log.debug('Got formList', body);
    prepareXMLResponse(res, odkRes.statusCode, body);
    res.send(body);
  });
});

const openRosaAcceptLength = (req, res, next) => {
  res.append(
    'X-OpenRosa-Accept-Content-Length',
    config.odk.SUBMISSION_MAX_LENGTH
  );
  next();
};

router.head('view/submissionList',
  openRosaAcceptLength,
  handleHeadRequest
);

router.get('/view/submissionList',
  openRosaAcceptLength,
  lowerCaseQueryKeys,
  (req, res, next) => {
    log.debug('Requesting ODK submission list');

    let formId = req.query.formid;
    let numEntries = req.query.numentries;
    let cursor = req.query.cursor;
    if (typeof formId !== 'undefined') {
      return aggregate.submissionList({formId, numEntries, cursor})
      .spread((odkRes, body) => {
        log.debug('Got submissionList', body);

        prepareXMLResponse(res, odkRes.statusCode, body);
        res.send(body);
      })
      .catch(next);
    } else {
      res.status(500).send('Error: Missing formid parameter');
    }
  }
);

router.head('/view/downloadSubmission', handleHeadRequest);

router.get('/view/downloadSubmission',
  lowerCaseQueryKeys,
  (req, res, next) => {
    let formId = req.query.formid;
    let topElement = req.query.topelement || formId;
    let submissionId = (
      req.query.submissionid ||
      req.query.idvalue ||
      req.query.instanceid
    );

    aggregate.downloadSubmission({formId, topElement, submissionId})
    .spread((odkRes, body) => {<commit_msg>bugfix: catch error on ODK formList endpoint<commit_after>'use strict';

const express = require('express');
const router = express.Router();
const BPromise = require('bluebird');
const formidable = require('formidable');
const config = require('config');
const log = require('server/util/logapp.js');
const string = require('common/string.js');
const normalizequery = require('server/middleware/normalizequery.js');
const aggregate = require('server/odk/aggregateapi.js');
const transform = require('server/odk/collect/collecttransform.js');
const aggregatesubmission = require('server/odk/aggregatesubmission.js');
const collect = require('server/odk/collect/collectsubmission.js');

const ODK_MASK_URL = config.server.PUBLIC_URL + '/odk';
log.info(`Backing up to ODK Aggregate -- ${config.odk.URL}`);
log.info(`Masking ODK Aggregate backup server with url ${ODK_MASK_URL}`);

const lowerCaseQueryKeys = normalizequery.lowerCaseQueryKeys();

const prepareXMLResponse = (res, statusCode, body) => {
  res.set({'Content-Type': 'text/xml', 'Content-Length': body.length});
  res.status(statusCode);
};

const handleHeadRequest = (req, res) => res.status(401).send('');

/**
 * To mimic the ODK Aggregate server this web server masks, the XML responses
 * with URLs for that original server must be altered to report this app's URL
 * in place of the original ODK Aggregate URL. This ensures that all requests
 * from the client arrive to this application first.
 *
 * @param  {string} xml      Original XML from Aggregate
 * @param  {string} fromHost URL to hide
 * @param  {string} toHost   URL to display
 * @return {string}          Altered XML
 */
const swapHostnames = (xml, fromHost, toHost) => {
  return new BPromise(resolve => {
    resolve(
      xml.replace(new RegExp(string.escapeRegExp(fromHost), 'g'), toHost)
      .replace(new RegExp('\/odk\:[0-9]{2,5}\/', 'g'), '/odk/')
    );
  });
};

router.all('*', aggregate.setOpenRosaHeaders);

router.head('/formList', handleHeadRequest);

router.get('/formList', lowerCaseQueryKeys, (req, res, next) => {
  let formId = req.query.formid;
  let verbose = req.query.verbose;
  let allVersions = req.query.listallversions;

  return aggregate.formList({formId, verbose, allVersions})
  .spread((odkRes, body) => {
    return BPromise.join(
      odkRes,
      swapHostnames(body, config.odk.URL, ODK_MASK_URL)
    );
  })
  .spread((odkRes, body) => {
    log.debug('Got formList', body);
    prepareXMLResponse(res, odkRes.statusCode, body);
    res.send(body);
  })
  .catch(next);
});

const openRosaAcceptLength = (req, res, next) => {
  res.append(
    'X-OpenRosa-Accept-Content-Length',
    config.odk.SUBMISSION_MAX_LENGTH
  );
  next();
};

router.head('view/submissionList',
  openRosaAcceptLength,
  handleHeadRequest
);

router.get('/view/submissionList',
  openRosaAcceptLength,
  lowerCaseQueryKeys,
  (req, res, next) => {
    log.debug('Requesting ODK submission list');

    let formId = req.query.formid;
    let numEntries = req.query.numentries;
    let cursor = req.query.cursor;
    if (typeof formId !== 'undefined') {
      return aggregate.submissionList({formId, numEntries, cursor})
      .spread((odkRes, body) => {
        log.debug('Got submissionList', body);

        prepareXMLResponse(res, odkRes.statusCode, body);
        res.send(body);
      })
      .catch(next);
    } else {
      res.status(400).send('Error: Missing formid parameter');
    }
  }
);

router.head('/view/downloadSubmission', handleHeadRequest);

router.get('/view/downloadSubmission',
  lowerCaseQueryKeys,
  (req, res, next) => {
    let formId = req.query.formid;
    let topElement = req.query.topelement || formId;
    let submissionId = (
      req.query.submissionid ||
      req.query.idvalue ||
      req.query.instanceid
    );

    aggregate.downloadSubmission({formId, topElement, submissionId})
    .spread((odkRes, body) => {
{'fraction_non_alphanumeric': 0.14663399535723498, 'fraction_numerical': 0.0025793139025019345, 'mean_word_length': 4.042262678803641, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3201691', 'n_tokens_mistral': 2527, 'n_tokens_neox': 2465, 'n_words': 564}