<commit_before>var command = require('../command');
var jsonParser = require('jsonparse');

var statusCodes = {
  SUCCESS: 0,
  INVALID_ARGUMENT: -22
};
var httpStatusCodes = {
  OK: 200,
  BAD_REQUEST: 400
};

function Loader(request, response, connection, logger) {
  this._request = request;
  this._response = response;
  this._connection = connection;
  this._logger = logger
  this._errorMessage = undefined;
}

Loader.prototype.run = function run() {
  this._startTimeInMilliseconds = Date.now();
  this._statusCode = statusCodes.SUCCESS;

  try {
    this._extractParameters();
  } catch (error) {
    this._statusCode = error.statusCode;
    this._errorMessage = error.message;
    this._sendResponse(0);
    return;
  }

  this._jsonParser = new jsonParser();
  this._nRecords = 0;
  this._nResponses = 0;
  this._nAdded = 0;
  this._isEnd = false;

  this._jsonParser.onValue = this._onValue.bind(this);
  this._request.on('data', function(chunk) {
    this._jsonParser.write(chunk);
  }.bind(this));
  this._request.once('end', function() {
    this._isEnd = true;

    if (this._nRecords == 0) {
      this._sendResponse([0]);
    }
  }.bind(this));
};

Loader.prototype._extractParameters = function _extractParameters() {
  var query = this._request.query;

  this._table = query.table;
  if (!this._table) {
    throw {
      statusCode: statusCodes.INVALID_ARGUMENT,
      messasge: 'required parameter is missing: <table>',
      httpStatusCode: httpStatusCodes.BAD_REQUEST
    };
  }

  if (query.columns) {
    this._columns = query.columns.split(/[,\s]+/);
  } else {
    this._columns = null;
  }
};

Loader.prototype._onValue = function _onValue(value) {
  if (this._jsonParser.stack.length != 1) {
    return;
  }

  if (Array.isArray(value) && !this._columns) {
    this._columns = value;
    return;
  }

  var message = {
    table: this._table,
    values: {}
  };

  this._nRecords++;

  if (Array.isArray(value)) {
    this._columns.forEach(function(column, i) {
      if (column == '_key') {
        message.key = value[i];
      } else {
        message.values[column] = value[i];
      }
    });
  } else {
    Object.keys(value).forEach(function(key) {
      if (key == '_key') {
        message.key = value[key];
      } else {
        message.values[key] = value[key];
      }
    });
  }

  this._connection.emit('add', message, function(error, message) {
    this._nResponses++;
    if (error) {
      this._logger.error("/d/load: failed to add a record", message);
    } else {
      var succeeded = message;
      if (succeeded) {
        this._nAdded++;
      }
    }
    if (this._isEnd && this._nRecords == this._nResponses) {
      this._sendResponse([this._nAdded]);
    }
  }.bind(this));
};

Loader.prototype._createResponse = function _createResponse(body) {
  var elapsedTimeInMilliseconds = Date.now() - this._startTimeInMilliseconds;
  var header = [
    this._statusCode,
    this._startTimeInMilliseconds / 1000,
    elapsedTimeInMilliseconds / 1000,
  ];
  if (this._errorMessage) {
    header.push(this._errorMessage);
  }
  return [header, body];
};

Loader.prototype._sendResponse = function _sendResponse(body) {
  var groongaResponse = this._createResponse(body);
  var groongaResponseHeader = groongaResponse[0];
  var httpStatusCode;
  if (groongaResponseHeader[0] == statusCodes.SUCCESS) {
    httpStatusCode = httpStatusCodes.OK;
  } else {
    httpStatusCode = httpStatusCodes.BAD_REQUEST;
  }
  this._response.jsonp(groongaResponse, httpStatusCode);
};

module.exports = {
  'groonga': new command.HTTPRequestResponse({
    path: '/d/:commandName',
    onRequest: function(request, connection) {
      connection.emit(request.params.commandName, request.query);
    }
  }),
  'groonga-load': new command.HTTPRequestResponse({
    method: 'POST',
    path:   '/d/load',
    onRequest: function(request, connection, response) {
      var loader = new Loader(request, response, connection, this.logger);
      loader.run();
    }
  })
};<commit_msg>groonga: Support ".json" optional suffix<commit_after>var command = require('../command');
var jsonParser = require('jsonparse');

var statusCodes = {
  SUCCESS: 0,
  INVALID_ARGUMENT: -22
};
var httpStatusCodes = {
  OK: 200,
  BAD_REQUEST: 400
};

function Loader(request, response, connection, logger) {
  this._request = request;
  this._response = response;
  this._connection = connection;
  this._logger = logger
  this._errorMessage = undefined;
}

Loader.prototype.run = function run() {
  this._startTimeInMilliseconds = Date.now();
  this._statusCode = statusCodes.SUCCESS;

  try {
    this._extractParameters();
  } catch (error) {
    this._statusCode = error.statusCode;
    this._errorMessage = error.message;
    this._sendResponse(0);
    return;
  }

  this._jsonParser = new jsonParser();
  this._nRecords = 0;
  this._nResponses = 0;
  this._nAdded = 0;
  this._isEnd = false;

  this._jsonParser.onValue = this._onValue.bind(this);
  this._request.on('data', function(chunk) {
    this._jsonParser.write(chunk);
  }.bind(this));
  this._request.once('end', function() {
    this._isEnd = true;

    if (this._nRecords == 0) {
      this._sendResponse([0]);
    }
  }.bind(this));
};

Loader.prototype._extractParameters = function _extractParameters() {
  var query = this._request.query;

  this._table = query.table;
  if (!this._table) {
    throw {
      statusCode: statusCodes.INVALID_ARGUMENT,
      messasge: 'required parameter is missing: <table>',
      httpStatusCode: httpStatusCodes.BAD_REQUEST
    };
  }

  if (query.columns) {
    this._columns = query.columns.split(/[,\s]+/);
  } else {
    this._columns = null;
  }
};

Loader.prototype._onValue = function _onValue(value) {
  if (this._jsonParser.stack.length != 1) {
    return;
  }

  if (Array.isArray(value) && !this._columns) {
    this._columns = value;
    return;
  }

  var message = {
    table: this._table,
    values: {}
  };

  this._nRecords++;

  if (Array.isArray(value)) {
    this._columns.forEach(function(column, i) {
      if (column == '_key') {
        message.key = value[i];
      } else {
        message.values[column] = value[i];
      }
    });
  } else {
    Object.keys(value).forEach(function(key) {
      if (key == '_key') {
        message.key = value[key];
      } else {
        message.values[key] = value[key];
      }
    });
  }

  this._connection.emit('add', message, function(error, message) {
    this._nResponses++;
    if (error) {
      this._logger.error("/d/load: failed to add a record", message);
    } else {
      var succeeded = message;
      if (succeeded) {
        this._nAdded++;
      }
    }
    if (this._isEnd && this._nRecords == this._nResponses) {
      this._sendResponse([this._nAdded]);
    }
  }.bind(this));
};

Loader.prototype._createResponse = function _createResponse(body) {
  var elapsedTimeInMilliseconds = Date.now() - this._startTimeInMilliseconds;
  var header = [
    this._statusCode,
    this._startTimeInMilliseconds / 1000,
    elapsedTimeInMilliseconds / 1000,
  ];
  if (this._errorMessage) {
    header.push(this._errorMessage);
  }
  return [header, body];
};

Loader.prototype._sendResponse = function _sendResponse(body) {
  var groongaResponse = this._createResponse(body);
  var groongaResponseHeader = groongaResponse[0];
  var httpStatusCode;
  if (groongaResponseHeader[0] == statusCodes.SUCCESS) {
    httpStatusCode = httpStatusCodes.OK;
  } else {
    httpStatusCode = httpStatusCodes.BAD_REQUEST;
  }
  this._response.jsonp(groongaResponse, httpStatusCode);
};

function handleHTTPRequest(request, connection) {
  connection.emit(request.params.commandName, request.query);
}

function handleLoadHTTPRequest(request, connection) {
  var loader = new Loader(request, response, connection, this.logger);
  loader.run();
}

module.exports = {
  'groonga': new command.HTTPRequestResponse({
    path: '/d/:commandName',
    onRequest: handleHTTPRequest
  }),
  'groonga': new command.HTTPRequestResponse({
    path: '/d/:commandName.json',
    onRequest: handleHTTPRequest
  }),
  'groonga-load': new command.HTTPRequestResponse({
    method: 'POST',
    path:   '/d/load',
    onRequest: handleLoadHTTPRequest
  }),
  'groonga-load': new command.HTTPRequestResponse({
    method: 'POST',
    path:   '/d/load.json',
    onRequest: handleLoadHTTPRequest
  })
};
{'fraction_non_alphanumeric': 0.14215686274509803, 'fraction_numerical': 0.006217120994739359, 'mean_word_length': 3.2722165474974463, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3595973', 'n_tokens_mistral': 2740, 'n_tokens_neox': 2692, 'n_words': 509}