<commit_before>/**
 * CouchDB Model
 * @module Model
 * @requires nano
 * @requires node.extend
 */

var extend = require('node.extend');
var uuid = require('node-uuid');
var string = require('underscore.string');
var Q = require('q');
var Buffer = require('buffer').Buffer;
var querystring = require('querystring');
require('array.prototype.find');

/**
 * Constructor for models
 * @constructor
 * @private
 * @param {object} databaseHandle a nano db handle (returned by nano.use(db)
 * @param {object} [options] options VO
 * @param {[object|string]} options.views array of view descriptors. 
 * A view descriptior can be either a string, or an object.
 * @param {string} options.views[] view URL, such as 
 * '_design/article/_view/by_date'
 * @param {string} options.views[].url view URL, 
 * such as '_design/article/_view/by_date'
 * @param {string} [options.views[].name] name of the view 
 * to be used in generated method names. Should be all-lowercase, underscored. 
 * If not set, the last portion of the view URL will be used.
 * @param {object} options.restapi define it to create request handler
 * @param {string} options.restapi.prefix URL prefix for API calls. E.g.
 * if prefix is `/mymodel`, `GET /mymodel/` will be treated as `GET /`. Do not
 * include trailing slash.
 * @param {boolean} options.restapi.index if true, the request handler will
 * return all documents on GET /
 * @param {boolean} options.restapi.byID enable `GET /{id} requests`
 */
function Model(databaseHandle, options) {
	this._useNanoDB(databaseHandle);
	this.instanceConstructor = Instance;

	this._views = {};

	// parse options
	if (options && Array.isArray(options.views)) {
		options.views.forEach(function(e) {
			var name, path;
			if (typeof e === "string") {
				// get the last portion of URL as the view name
				name = e.split('/').slice(-1)[0];
				path = e;
			} else if (typeof e === "object") {
				name = e.name || e.path.split('/').slice(-1)[0];
				path = e.path;
			}

			this._views[name] = {
				path: path,
				name: name,
				findOne: null,
				findMany: null
			};
		}.bind(this));
	}

	if (options && options.restapi && typeof options.restapi === 'object') {
		this.onRequest = createRequestHandler(this);
	}

	// create request handler
	function createRequestHandler(model) {
		var apiOptions = options.restapi;

		return function onRequest(request, response, next, __test__) {
			var path = request.url;
			var params;

			function respondError(code, reason) {
				var body = JSON.stringify({
					error: code,
					reson: reason
				});

				response.writeHead(code, reason, {
					'Content-Type': 'application/json',
					'Content-Length': Buffer.byteLength(body)
				});
				response.end(body);
			}

			function respondResult(body, code, reason) {
				body = JSON.stringify(body);

				if (!code) {
					code = 200;
				}

				response.writeHead(code, reason, {
					'Content-Type': 'application/json',
					'Content-Length': Buffer.byteLength(body)
				});

				response.end(body);
			}

			if (apiOptions.prefix) {
				path = path.replace(new RegExp('^' + apiOptions.prefix), '');
			}

			if (request.method === 'GET' && path === '/') { // index
				if (!apiOptions.index) {
					respondError(403, 'Indexing Not Allowed');
				} else {
					model.findAll(function(error, result) {
						if (error) {
							respondError(500, 'Database Error');									  
						} else {
							respondResult(result.map(function(e) {
								return e.toVO();					  
							}));
						}
					});
				}
			} else if (request.method === 'GET' && path.match(/^\/[^\/]+$/)) { // findOneByID
				if (!apiOptions.byID) {
					respondError(403, 'ByID Queries Not Allowed');
				} else {
					model.findOneByID(path.replace(/^\//, ''), 
						function(error, result) {
						if (error && error.status_code === 404) {
							respondError(404, 'Not Found');
						} else if (error) {
							respondError(500, 'Database Error');									  
						} else {
							respondResult(result.toVO());
						}
					});
				}
			} else if ((request.method === 'PUT' || request.method === 'POST' )
				&& path === '/') {
				if (!apiOptions.save) {
					respondError(403, 'Save Is Not Allowed');
				} else {
					(function() {
						var body = '';

						function processRequest() {
							body = model.create(JSON.parse(body));

							model.save(body, function(error, response) {
								if (error) {
									if (error.status_code === 409) {
										respondError(409, 'Conflict');
									} else {
										respondError(500, 'Database Error');
									}
								} else {
									respondResult(response);
								}
							});
						}

						if (!__test__) {
							request.on('data', function(data) {
								body += data;
							});<commit_msg>Added error handling for invalid JSON<commit_after>/**
 * CouchDB Model
 * @module Model
 * @requires nano
 * @requires node.extend
 */

var extend = require('node.extend');
var uuid = require('node-uuid');
var string = require('underscore.string');
var Q = require('q');
var Buffer = require('buffer').Buffer;
var querystring = require('querystring');
require('array.prototype.find');

/**
 * Constructor for models
 * @constructor
 * @private
 * @param {object} databaseHandle a nano db handle (returned by nano.use(db)
 * @param {object} [options] options VO
 * @param {[object|string]} options.views array of view descriptors. 
 * A view descriptior can be either a string, or an object.
 * @param {string} options.views[] view URL, such as 
 * '_design/article/_view/by_date'
 * @param {string} options.views[].url view URL, 
 * such as '_design/article/_view/by_date'
 * @param {string} [options.views[].name] name of the view 
 * to be used in generated method names. Should be all-lowercase, underscored. 
 * If not set, the last portion of the view URL will be used.
 * @param {object} options.restapi define it to create request handler
 * @param {string} options.restapi.prefix URL prefix for API calls. E.g.
 * if prefix is `/mymodel`, `GET /mymodel/` will be treated as `GET /`. Do not
 * include trailing slash.
 * @param {boolean} options.restapi.index if true, the request handler will
 * return all documents on GET /
 * @param {boolean} options.restapi.byID enable `GET /{id} requests`
 */
function Model(databaseHandle, options) {
	this._useNanoDB(databaseHandle);
	this.instanceConstructor = Instance;

	this._views = {};

	// parse options
	if (options && Array.isArray(options.views)) {
		options.views.forEach(function(e) {
			var name, path;
			if (typeof e === "string") {
				// get the last portion of URL as the view name
				name = e.split('/').slice(-1)[0];
				path = e;
			} else if (typeof e === "object") {
				name = e.name || e.path.split('/').slice(-1)[0];
				path = e.path;
			}

			this._views[name] = {
				path: path,
				name: name,
				findOne: null,
				findMany: null
			};
		}.bind(this));
	}

	if (options && options.restapi && typeof options.restapi === 'object') {
		this.onRequest = createRequestHandler(this);
	}

	// create request handler
	function createRequestHandler(model) {
		var apiOptions = options.restapi;

		return function onRequest(request, response, next, __test__) {
			var path = request.url;
			var params;

			function respondError(code, reason) {
				var body = JSON.stringify({
					error: code,
					reason: reason
				});

				response.writeHead(code, reason, {
					'Content-Type': 'application/json',
					'Content-Length': Buffer.byteLength(body)
				});
				response.end(body);
			}

			function respondResult(body, code, reason) {
				body = JSON.stringify(body);

				if (!code) {
					code = 200;
				}

				response.writeHead(code, reason, {
					'Content-Type': 'application/json',
					'Content-Length': Buffer.byteLength(body)
				});

				response.end(body);
			}

			if (apiOptions.prefix) {
				path = path.replace(new RegExp('^' + apiOptions.prefix), '');
			}

			if (request.method === 'GET' && path === '/') { // index
				if (!apiOptions.index) {
					respondError(403, 'Indexing Not Allowed');
				} else {
					model.findAll(function(error, result) {
						if (error) {
							respondError(500, 'Database Error');									  
						} else {
							respondResult(result.map(function(e) {
								return e.toVO();					  
							}));
						}
					});
				}
			} else if (request.method === 'GET' && path.match(/^\/[^\/]+$/)) { // findOneByID
				if (!apiOptions.byID) {
					respondError(403, 'ByID Queries Not Allowed');
				} else {
					model.findOneByID(path.replace(/^\//, ''), 
						function(error, result) {
						if (error && error.status_code === 404) {
							respondError(404, 'Not Found');
						} else if (error) {
							respondError(500, 'Database Error');									  
						} else {
							respondResult(result.toVO());
						}
					});
				}
			} else if ((request.method === 'PUT' || request.method === 'POST' )
				&& path === '/') {
				if (!apiOptions.save) {
					respondError(403, 'Save Is Not Allowed');
				} else {
					(function() {
						var body = '';
						var parseError;

						function processRequest() {
							try {
								body = model.create(JSON.parse(body));
							} catch (error) {
								body = null;
							}

							if (body) {
								model.save(body, function(error, response) {
									if (error) {
										if (error.status_code === 409) {
											respondError(409, 'Conflict');
										} else {
											respondError(500, 'Database Error');
										}
									} else {
										respondResult(response);
									}
								});
							} else {
								respondError(400, 'Invalid JSON');
							}
						}

						if (!__test__) {
							request.on('data', function(data) {
								body += data;
							});
{'fraction_non_alphanumeric': 0.15459983498349836, 'fraction_numerical': 0.007941419141914192, 'mean_word_length': 2.662009063444109, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '2444422', 'n_tokens_mistral': 3999, 'n_tokens_neox': 2979, 'n_words': 794}