<commit_before>/**
 * express-restify-mongoose.js
 *
 * Copyright (C) 2013 by <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the 'Software'), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/
'use strict';

var util = require('util'),
    Filter = require('./resource_filter'),
    permissions = require('./permissions'),
    _ = require('lodash'),
    inflection = require('inflection'),
    http = require('http'),
    customDefaults = null;

function getDefaults() {
    var options = {
        prefix: '/api',
        version: '/v1',
        private: false,
        lean: true,
        plural: true,
        middleware: [],
        strict: false,
        findOneAndUpdate: true,
        findOneAndRemove: true,
        contextFilter: null,
        postCreate: null
    };

    for (var prop in customDefaults) {
        if (customDefaults[prop] instanceof Array) {
			options[prop] = customDefaults[prop].slice(0);
		} else {
	        options[prop] = customDefaults[prop];
		}
    }

    return options;
}

function outputExpress(res, result, statusCode) {
    if (result !== null) {
        res.status(statusCode || 200).json(result);
    } else {
        res.status(statusCode || 200).end();
    }
}

function outputRestify(res, result, statusCode) {
    if (result !== null) {
        res.send(statusCode || 200, result);
    } else {
        res.send(statusCode || 200);
    }
}

var restify = function(app, model, opts) {
    var postProcess, lean, filter, contextFilter, postCreate, postDelete,
        usingExpress,
        options = getDefaults(),
        queryOptions = {
            protected: ['skip', 'limit', 'sort', 'distinct', 'populate', 'select', 'lean',
                '$and', '$or', 'query', 'projection'],//H+ exposes OR, AND and WHERE methods
            current: {}
        };

    for (var prop in opts) {
        if (opts[prop] instanceof Array) {
            options[prop] = [];
            for (var index in opts[prop]) {
                options[prop][index] = opts[prop][index];
            }
        } else {
            options[prop] = opts[prop];
        }
    }

    options.private = options.private ? options.private.split(',') : [];
    options.protected = options.protected ?
        options.protected.split(',') : [];

    model.schema.eachPath(function(name, path) {
        if (path.options.access) {
            switch (path.options.access.toLowerCase()) {
                case 'private':
                    options.private.push(name);
                    break;
                case 'protected':
                    options.protected.push(name);
                    break;
            }
        }
    });

    if (options.exclude) {
        options.private = options.exclude.split(',');
        console.error('Exclude is deprecated. Use private instead');
    }

    lean = options.lean;
    filter = new Filter(model, options.private, options.protected);
    postProcess = options.postProcess || function() {
    };

    if (options.middleware) {
        if (!(options.middleware instanceof Array)) {
            var m = options.middleware;
            options.middleware = [ m ];
        }
    }

    function cleanQuery(req, res, next) {
        queryOptions.current = {};
        queryOptions.clean = _.clone(req.query);
        var err = null;
        for (var key in queryOptions.clean) {
            if (queryOptions.protected.indexOf(key) !== -1) {
                queryOptions.current[key] = queryOptions.clean[key];
                delete queryOptions.clean[key];
            } else if (!model.schema.paths.hasOwnProperty(key)) {
                var keys = key.match('.') &&
                    !model.schema.paths.hasOwnProperty(key) ?
                        key.split('.') : false;
                if(!keys || !model.schema.paths.hasOwnProperty(keys[0]) ||
                    !model.schema.paths[keys[0]].schema.paths.hasOwnProperty(keys[1]) ||
                    keys.length > 2) {

                    if(!model.schema.virtuals.hasOwnProperty(keys[0])) {
                        err = new Error(http.STATUS_CODES[400]);
                        err.status = 400;
                        break;
                    }
                }
            }
        }

        if(err) {
            onError(err, req, res, next);
        } else {
            next();
        }
    }

    usingExpress = !options.restify;

    var onError = options.onError ? options.onError : function(err, req, res, next) {
        var errorString;
        if (options.fullErrors) {
            res.setHeader('Content-Type', 'application/json');
            errorString = JSON.stringify(err);
        } else {
            errorString = err.message;
        }

        if (usingExpress) {
            res.status(err.status).send(errorString);
        } else {
            res.send(err.status, errorString);<commit_msg>refactor(error): default to passing full error<commit_after>/**
 * express-restify-mongoose.js
 *
 * Copyright (C) 2013 by <NAME>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the 'Software'), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/
'use strict';

var util = require('util'),
    Filter = require('./resource_filter'),
    permissions = require('./permissions'),
    _ = require('lodash'),
    inflection = require('inflection'),
    http = require('http'),
    customDefaults = null;

function getDefaults() {
    var options = {
        prefix: '/api',
        version: '/v1',
        private: false,
        lean: true,
        plural: true,
        middleware: [],
        strict: false,
        findOneAndUpdate: true,
        findOneAndRemove: true,
        contextFilter: null,
        postCreate: null
    };

    for (var prop in customDefaults) {
        if (customDefaults[prop] instanceof Array) {
			options[prop] = customDefaults[prop].slice(0);
		} else {
	        options[prop] = customDefaults[prop];
		}
    }

    return options;
}

function outputExpress(res, result, statusCode) {
    if (result !== null) {
        res.status(statusCode || 200).json(result);
    } else {
        res.status(statusCode || 200).end();
    }
}

function outputRestify(res, result, statusCode) {
    if (result !== null) {
        res.send(statusCode || 200, result);
    } else {
        res.send(statusCode || 200);
    }
}

var restify = function(app, model, opts) {
    var postProcess, lean, filter, contextFilter, postCreate, postDelete,
        usingExpress,
        options = getDefaults(),
        queryOptions = {
            protected: ['skip', 'limit', 'sort', 'distinct', 'populate', 'select', 'lean',
                '$and', '$or', 'query', 'projection'],//H+ exposes OR, AND and WHERE methods
            current: {}
        };

    for (var prop in opts) {
        if (opts[prop] instanceof Array) {
            options[prop] = [];
            for (var index in opts[prop]) {
                options[prop][index] = opts[prop][index];
            }
        } else {
            options[prop] = opts[prop];
        }
    }

    options.private = options.private ? options.private.split(',') : [];
    options.protected = options.protected ?
        options.protected.split(',') : [];

    model.schema.eachPath(function(name, path) {
        if (path.options.access) {
            switch (path.options.access.toLowerCase()) {
                case 'private':
                    options.private.push(name);
                    break;
                case 'protected':
                    options.protected.push(name);
                    break;
            }
        }
    });

    if (options.exclude) {
        options.private = options.exclude.split(',');
        console.error('Exclude is deprecated. Use private instead');
    }

    lean = options.lean;
    filter = new Filter(model, options.private, options.protected);
    postProcess = options.postProcess || function() {
    };

    if (options.middleware) {
        if (!(options.middleware instanceof Array)) {
            var m = options.middleware;
            options.middleware = [ m ];
        }
    }

    function cleanQuery(req, res, next) {
        queryOptions.current = {};
        queryOptions.clean = _.clone(req.query);
        var err = null;
        for (var key in queryOptions.clean) {
            if (queryOptions.protected.indexOf(key) !== -1) {
                queryOptions.current[key] = queryOptions.clean[key];
                delete queryOptions.clean[key];
            } else if (!model.schema.paths.hasOwnProperty(key)) {
                var keys = key.match('.') &&
                    !model.schema.paths.hasOwnProperty(key) ?
                        key.split('.') : false;
                if(!keys || !model.schema.paths.hasOwnProperty(keys[0]) ||
                    !model.schema.paths[keys[0]].schema.paths.hasOwnProperty(keys[1]) ||
                    keys.length > 2) {

                    if(!model.schema.virtuals.hasOwnProperty(keys[0])) {
                        err = new Error(http.STATUS_CODES[400]);
                        err.status = 400;
                        break;
                    }
                }
            }
        }

        if(err) {
            onError(err, req, res, next);
        } else {
            next();
        }
    }

    usingExpress = !options.restify;

    var onError = options.onError ? options.onError : function(err, req, res, next) {
        var errorString;
    	res.setHeader('Content-Type', 'application/json');
    	errorString = JSON.stringify(err);

        if (usingExpress) {
            res.status(err.status).send(errorString);
        } else {
            res.send(err.status, errorString);
{'fraction_non_alphanumeric': 0.12040798686144005, 'fraction_numerical': 0.005186273662373585, 'mean_word_length': 2.073857598299681, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, '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': '2207184', 'n_tokens_mistral': 3205, 'n_tokens_neox': 3056, 'n_words': 867}