UNPKG

sails-hook-orm

Version:

The ORM hook from Sails core.

776 lines (668 loc) 52.6 kB
/** * Module dependencies */ var util = require('util'); var _ = require('@sailshq/lodash'); var flaverr = require('flaverr'); var VALIDATIONS = require('waterline/accessible/allowed-validations'); var DEPRECATED_VALIDATIONS = require('../constants/deprecated-validations.list'); var UNSUPPORTED_VALIDATIONS = require('../constants/invalid-validations.list'); var modelHasNoDatastoreError = require('../constants/model-has-no-datastore.error'); var constructError = require('./construct-error'); /** * validateModelDef() * * Validate, normalize, and mix in implicit defaults for a particular model * definition. Includes adjustments for backwards compatibility. * * @required {Dictionary} originalModelDef * @required {String} modelIdentity * @required {Dictionary} hook * @required {SailsApp} sails * * @returns {Dictionary} [normalized model definition] * @throws {Error} E_MODEL_HAS_MULTIPLE_DATASTORES * @throws {Error} E_MODEL_HAS_NO_DATASTORE */ module.exports = function validateModelDef (originalModelDef, modelIdentity, hook, sails) { // ██╗ ██╗ █████╗ ██╗ ██╗██████╗ █████╗ ████████╗███████╗ // ██║ ██║██╔══██╗██║ ██║██╔══██╗██╔══██╗╚══██╔══╝██╔════╝ // ██║ ██║███████║██║ ██║██║ ██║███████║ ██║ █████╗ // ╚██╗ ██╔╝██╔══██║██║ ██║██║ ██║██╔══██║ ██║ ██╔══╝ // ╚████╔╝ ██║ ██║███████╗██║██████╔╝██║ ██║ ██║ ███████╗ // ╚═══╝ ╚═╝ ╚═╝╚══════╝╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ // // ██╗██████╗ ███████╗███╗ ██╗████████╗██╗████████╗██╗ ██╗ // ██║██╔══██╗██╔════╝████╗ ██║╚══██╔══╝██║╚══██╔══╝╚██╗ ██╔╝ // ██║██║ ██║█████╗ ██╔██╗ ██║ ██║ ██║ ██║ ╚████╔╝ // ██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║ ██║ ╚██╔╝ // ██║██████╔╝███████╗██║ ╚████║ ██║ ██║ ██║ ██║ // ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ if (!modelIdentity.match(/^[a-z_][a-z0-9_]*$/)) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'The `' + originalModelDef.globalId + '` model has an invalid name.\n'+ 'Model names must start with a letter and contain only letters, numbers and underscores.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // Rebuild model definition to provide a layer of insulation against any // changes that might tamper with the original, raw definition. // // Model settings are determined using the following rules: // (in descending order of precedence) // • explicit model def // • sails.config.models // • implicit framework defaults var normalizedModelDef; // We start off with some implicit defaults: normalizedModelDef = { // Set `identity` so it is available on the model itself. identity: modelIdentity, // Default the table name to the identity. tableName: modelIdentity, // Default attributes to an empty dictionary (`{}`). // > Note that we handle merging attributes as a special case below // > (i.e. because we're doing a shallow `.extend()` rather than a deep merge) // > This allows app-wide defaults to include attributes that will be shared across // > all models. attributes: {} }; // ███╗ ███╗███████╗██████╗ ██████╗ ███████╗ // ████╗ ████║██╔════╝██╔══██╗██╔════╝ ██╔════╝ // ██╔████╔██║█████╗ ██████╔╝██║ ███╗█████╗ // ██║╚██╔╝██║██╔══╝ ██╔══██╗██║ ██║██╔══╝ // ██║ ╚═╝ ██║███████╗██║ ██║╚██████╔╝███████╗ // ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝ // // ██████╗ ███████╗███████╗ █████╗ ██╗ ██╗██╗ ████████╗███████╗ // ██╔══██╗██╔════╝██╔════╝██╔══██╗██║ ██║██║ ╚══██╔══╝██╔════╝ // ██║ ██║█████╗ █████╗ ███████║██║ ██║██║ ██║ ███████╗ // ██║ ██║██╔══╝ ██╔══╝ ██╔══██║██║ ██║██║ ██║ ╚════██║ // ██████╔╝███████╗██║ ██║ ██║╚██████╔╝███████╗██║ ███████║ // ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚══════╝ // // Next, merge in app-wide defaults. _.extend(normalizedModelDef, _.omit(sails.config.models, ['attributes'])); // Merge in attributes from app-wide defaults, if there are any. if (!_.isFunction(sails.config.models.attributes) && !_.isArray(sails.config.models.attributes) && _.isObject(sails.config.models.attributes)) { normalizedModelDef.attributes = _.extend(normalizedModelDef.attributes, sails.config.models.attributes); } // Finally, fold in the original properties provided in the userland model definition. _.extend(normalizedModelDef, _.omit(originalModelDef, ['attributes'])); // Merge in attributes from the original model def, if there are any. if (!_.isFunction(originalModelDef.attributes) && !_.isArray(originalModelDef.attributes) && _.isObject(originalModelDef.attributes)) { normalizedModelDef.attributes = _.extend(normalizedModelDef.attributes, originalModelDef.attributes); } // If there's a top-level `connection`, bail out (should be `datastore`). if (normalizedModelDef.connection) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In model `' + modelIdentity + '`:\n'+ 'The `connection` setting is no longer supported. Please use `datastore` instead.\n'+ 'See https://sailsjs.com/docs/concepts/models-and-orm/model-settings for more info.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // If there's a top-level `autoCreatedAt`, `autoUpdatedAt` or `autoPK` model setting, bail out. if (!_.isUndefined(normalizedModelDef.autoCreatedAt) || !_.isUndefined(normalizedModelDef.autoUpdatedAt) || !_.isUndefined(normalizedModelDef.autoPK)) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In model `' + modelIdentity + '`:\n'+ 'The `autoCreatedAt`, `autoUpdatedAt` and `autoPK` top-level model settings were removed\n'+ 'in Sails 1.0. See http://sailsjs.com/docs/concepts/models-and-orm/attributes for info\n'+ 'on configuring attributes to be timestamps. For info on changing the primary key of a model,\n'+ 'see https://sailsjs.com/docs/concepts/models-and-orm/model-settings#?primarykey.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // If there's a top-level `types` model setting, bail out. if (!_.isUndefined(normalizedModelDef.types)) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In model `' + modelIdentity + '`:\n'+ 'The `types` model setting was removed in Sails 1.0. To perform custom validation on\n'+ 'an attribute, set `validations: { custom: true }` on that attribute.\n'+ 'See https://sailsjs.com/docs/concepts/models-and-orm/validations for info\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } if (!normalizedModelDef.attributes[normalizedModelDef.primaryKey]) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In model `' + modelIdentity + '`:\n'+ 'The primary key is set to `' + normalizedModelDef.primaryKey + '`, but no such attribute was found on the model.\n'+ 'You must define an `' + normalizedModelDef.primaryKey + '` attribute in `api/' + originalModelDef.globalId + '.js` or in `config/models.js`.\n'+ 'See http://sailsjs.com/upgrading#?changes-to-model-configuration for info\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // ███╗ ██╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ██╗ ██╗███████╗███████╗ // ████╗ ██║██╔═══██╗██╔══██╗████╗ ████║██╔══██╗██║ ██║╚══███╔╝██╔════╝ // ██╔██╗ ██║██║ ██║██████╔╝██╔████╔██║███████║██║ ██║ ███╔╝ █████╗ // ██║╚██╗██║██║ ██║██╔══██╗██║╚██╔╝██║██╔══██║██║ ██║ ███╔╝ ██╔══╝ // ██║ ╚████║╚██████╔╝██║ ██║██║ ╚═╝ ██║██║ ██║███████╗██║███████╗███████╗ // ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚══════╝ // // █████╗ ████████╗████████╗██████╗ ██╗██████╗ ██╗ ██╗████████╗███████╗███████╗ // ██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██║██╔══██╗██║ ██║╚══██╔══╝██╔════╝██╔════╝ // ███████║ ██║ ██║ ██████╔╝██║██████╔╝██║ ██║ ██║ █████╗ ███████╗ // ██╔══██║ ██║ ██║ ██╔══██╗██║██╔══██╗██║ ██║ ██║ ██╔══╝ ╚════██║ // ██║ ██║ ██║ ██║ ██║ ██║██║██████╔╝╚██████╔╝ ██║ ███████╗███████║ // ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚══════╝╚══════╝ // A mapping of formerly-acceptable types to the currently-accepted types. var typeMapping = { text: 'string', integer: 'number', float: 'number', date: 'number', datetime: 'number', binary: 'ref', array: 'json', mediumtext: 'string', longtext: 'string', objectId: 'string' }; var noLongerSupportedTypes = { array: { suggestType: 'json', suggestColumnType: 'array' }, date: { suggestType: 'string', suggestColumnType: 'date' }, datetime: { suggestType: 'string', suggestColumnType: 'datetime' }, binary: { suggestType: 'ref', suggestColumnType: 'binary' }, objectid: { suggestType: 'ref', suggestColumnType: 'objectid' } }; // Loop through and normalize each attribute in the model. _.each(normalizedModelDef.attributes, function updateProperties (val, attributeName) { // If an attribute is set to `false`, delete it from the model. if (val === false) { delete normalizedModelDef.attributes[attributeName]; return; } // ┬ ┬┌─┐┌┐┌┌┬┐┬ ┌─┐ ┬┌┐┌┌─┐┌┬┐┌─┐┌┐┌┌─┐┌─┐ ┌┬┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐┌─┐ // ├─┤├─┤│││ │││ ├┤ ││││└─┐ │ ├─┤││││ ├┤ │││├┤ │ ├─┤│ │ ││└─┐ // ┴ ┴┴ ┴┘└┘─┴┘┴─┘└─┘ ┴┘└┘└─┘ ┴ ┴ ┴┘└┘└─┘└─┘ ┴ ┴└─┘ ┴ ┴ ┴└─┘─┴┘└─┘ // Always ignore `toJSON` for now. if (attributeName === 'toJSON') { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In model `' + modelIdentity + '`:\n'+ 'The `toJSON` instance method is no longer supported.\n'+ 'Instead, please use the `customToJSON` model setting.\n'+ 'See http://sailsjs.com/docs/concepts/models-and-orm/model-settings for more info.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // Ignore `protected` attribute modifier if (val.protected) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In model `' + modelIdentity + '`:\n'+ 'The `protected` attribute modifier is no longer supported.\n'+ 'Instead, please use the `customToJSON` model setting to filter out attributes.\n'+ 'See http://sailsjs.com/docs/concepts/models-and-orm/model-settings for more info.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // If the attribute is a function, log a message if (_.isFunction(val)) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'Model instance methods are no longer supported in Sails v1.\n'+ 'Please refactor the logic from this instance method into\n'+ 'a static method, model method or helper.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // ┬ ┬┌─┐┌┐┌┌┬┐┬ ┌─┐ ┌┬┐┌─┐┌─┐┌─┐┬ ┬┬ ┌┬┐┌─┐╔╦╗┌─┐ ┌─┐┌┐┌┌─┐ // ├─┤├─┤│││ │││ ├┤ ││├┤ ├┤ ├─┤│ ││ │ └─┐ ║ │ │ ├┤ │││└─┐ // ┴ ┴┴ ┴┘└┘─┴┘┴─┘└─┘ ─┴┘└─┘└ ┴ ┴└─┘┴─┘┴ └─┘ ╩ └─┘ └ ┘└┘└─┘ // Throw an error if a "defaultsTo" function is detected. if (_.isFunction(val.defaultsTo)) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The `defaultsTo` property can no longer be specified as a function in Sails 1.0. If you\n'+ 'need to calculate a value for the attribute before creating a record, try wrapping your\n'+ '`create` logic in a helper (see http://sailsjs.com/docs/concepts/helpers)\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // ┬ ┬┌─┐┌┐┌┌┬┐┬ ┌─┐ ┌─┐┬─┐┬┌┬┐┌─┐┬─┐┬ ┬╦╔═┌─┐┬ ┬ ┌─┐┬─┐┌─┐┌─┐ // ├─┤├─┤│││ │││ ├┤ ├─┘├┬┘││││├─┤├┬┘└┬┘╠╩╗├┤ └┬┘ ├─┘├┬┘│ │├─┘ // ┴ ┴┴ ┴┘└┘─┴┘┴─┘└─┘ ┴ ┴└─┴┴ ┴┴ ┴┴└─ ┴ ╩ ╩└─┘ ┴ ┴ ┴└─└─┘┴ // Throw an error if a `primaryKey` attribute is declared on an attribute. if (!_.isUndefined(val.primaryKey)) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The `primaryKey` property can no longer be specified on an attribute in Sails 1.0.\n'+ 'If you want to declare `' + attributeName + '` to be the primary key of `' + modelIdentity + '`,\n' + 'set `primaryKey: \'' + attributeName + '\'` at the top level of the model.\n' + '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // ┌─┐─┐ ┬┌─┐┌─┐┌┐┌┌┬┐ ┌┬┐┬ ┬┌─┐┌─┐ ┌─┐┬ ┬┌─┐┬─┐┌┬┐┌─┐┬ ┬┌┬┐┌─┐ // ├┤ ┌┴┬┘├─┘├─┤│││ ││ │ └┬┘├─┘├┤ └─┐├─┤│ │├┬┘ │ │ │ │ │ └─┐ // └─┘┴ └─┴ ┴ ┴┘└┘─┴┘ ┴ ┴ ┴ └─┘ └─┘┴ ┴└─┘┴└─ ┴ └─┘└─┘ ┴ └─┘ // If the attribute value is a string, expand it into a type. if (_.isString(val)) { normalizedModelDef.attributes[attributeName] = { type: val }; val = normalizedModelDef.attributes[attributeName]; } // ┌─┐┌┐┌┌─┐┬ ┬┬─┐┌─┐ ┌┬┐┬ ┬┌─┐┌─┐ ┌─┐┬─┐ ┌─┐┌─┐┌─┐┌─┐┌─┐┬┌─┐┌┬┐┬┌─┐┌┐┌ // ├┤ │││└─┐│ │├┬┘├┤ │ └┬┘├─┘├┤ │ │├┬┘ ├─┤└─┐└─┐│ ││ │├─┤ │ ││ ││││ // └─┘┘└┘└─┘└─┘┴└─└─┘ ┴ ┴ ┴ └─┘ └─┘┴└─ ┴ ┴└─┘└─┘└─┘└─┘┴┴ ┴ ┴ ┴└─┘┘└┘ // ┌┬┐┌─┐┌─┐┬ ┌─┐┬─┐┌─┐┌┬┐┬┌─┐┌┐┌ // ││├┤ │ │ ├─┤├┬┘├─┤ │ ││ ││││ // ─┴┘└─┘└─┘┴─┘┴ ┴┴└─┴ ┴ ┴ ┴└─┘┘└┘ if ( // If `type` is used, it must be a non-empty string. (!_.isUndefined(val.type) && (!_.isString(val.type) || val.type === '')) || // Otherwise, `model` or `collection` must be specified. (_.isUndefined(val.type) && _.isUndefined(val.model) && _.isUndefined(val.collection) ) ) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'Attributes must have either a `type` property that is a non-empty string declaring\n'+ 'the attribute\'s data type, a `model` property declaring a singular association with\n'+ 'another model, or a `collection` property declaring a plural assocation with another model.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // ┌┐┌┌─┐┬─┐┌┬┐┌─┐┬ ┬┌─┐┌─┐ ┌┬┐┬ ┬┌─┐┌─┐ ┌┐┌┌─┐┌┬┐┌─┐┌─┐ // ││││ │├┬┘│││├─┤│ │┌─┘├┤ │ └┬┘├─┘├┤ │││├─┤│││├┤ └─┐ // ┘└┘└─┘┴└─┴ ┴┴ ┴┴─┘┴└─┘└─┘ ┴ ┴ ┴ └─┘ ┘└┘┴ ┴┴ ┴└─┘└─┘ // Make sure all type names are lowercased (so that using `type: 'STRING'` doesn't throw an error). if (!_.isUndefined(val.type)) { val.type = val.type.toLowerCase(); } // ┬─┐┌─┐┌┬┐┌─┐┬ ┬┌─┐ ┌─┐┌─┐┌─┐┌─┐┌─┐┬┌─┐┌┬┐┬┌─┐┌┐┌ ┬ ┬┌─┐┬ ┬┌┬┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ // ├┬┘├┤ ││││ │└┐┌┘├┤ ├─┤└─┐└─┐│ ││ │├─┤ │ ││ ││││ └┐┌┘├─┤│ │ ││├─┤ │ ││ ││││└─┐ // ┴└─└─┘┴ ┴└─┘ └┘ └─┘ ┴ ┴└─┘└─┘└─┘└─┘┴┴ ┴ ┴ ┴└─┘┘└┘ └┘ ┴ ┴┴─┘┴─┴┘┴ ┴ ┴ ┴└─┘┘└┘└─┘ // Associations don't need `validations` dictionaries, so complain if we see them. if (val.collection || val.model) { if (!_.isUndefined(val.validations)) { sails.log.debug('In attribute `' + attributeName + '` of model `' + modelIdentity + '`:'); sails.log.debug('The `validations` property is not valid for associations. Ignoring...\n'); delete val.validations; } } // ┬ ┬┌─┐┌┐┌┌┬┐┬ ┌─┐ ┌┬┐┌─┐┌─┐ ┬ ┌─┐┬ ┬┌─┐┬ ┬ ┬┌─┐┬ ┬┌┬┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ // ├─┤├─┤│││ │││ ├┤ │ │ │├─┘───│ ├┤ └┐┌┘├┤ │ └┐┌┘├─┤│ │ ││├─┤ │ ││ ││││└─┐ // ┴ ┴┴ ┴┘└┘─┴┘┴─┘└─┘ ┴ └─┘┴ ┴─┘└─┘ └┘ └─┘┴─┘ └┘ ┴ ┴┴─┘┴─┴┘┴ ┴ ┴ ┴└─┘┘└┘└─┘ // First, loop through each property and if it's a validation or deprecated validation, massage it // into the `validations` property. _.each(val, function handleTopLevelValidations(property, propertyName) { // Is this property a supported validation? if (VALIDATIONS[propertyName]) { // For associations, that was a trick question. No validations are valid! if (val.collection || val.model) { sails.log.debug('In attribute `' + attributeName + '` of model `' + modelIdentity + '`:'); sails.log.debug('The `'+ propertyName+ '` property is not valid for associations. Ignoring...\n'); delete val[propertyName]; return; } // Move the validation into the `validations` dictionary. val.validations = val.validations || {}; val.validations[propertyName] = property; delete val[propertyName]; return; } // Ok, maybe it's a deprecated validation? if (DEPRECATED_VALIDATIONS[propertyName]) { // For associations, that was a trick question. No validations are valid! if (val.collection || val.model) { sails.log.debug('In attribute `' + attributeName + '` of model `' + modelIdentity + '`:'); sails.log.debug(' The `'+ propertyName+ '` property is not valid for associations. Ignoring...\n'); delete val[propertyName]; return; } // Log a deprecation message and move the NEW version of the validation into a `validations` dictionary. sails.log.debug('In attribute `' + attributeName + '` of model `' + modelIdentity + '`:'); sails.log.debug(' The `' + propertyName + '` validation rule is now `' + DEPRECATED_VALIDATIONS[propertyName] + '` (changing it for you this time).\n'); val.validations = val.validations || {}; val.validations[DEPRECATED_VALIDATIONS[propertyName]] = property; delete val[propertyName]; return; } // Ok, is it a no-longer-supported validation? if (UNSUPPORTED_VALIDATIONS[propertyName]) { // If so, throw a nicer error than waterline-schema would. throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The `' + propertyName + '` validation is no longer supported.\n'+ 'Try using a custom validation instead!\n'+ 'See http://sailsjs.com/docs/concepts/models-and-orm/validations for info\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // Fine I give up. We'll let waterline-schema determine whether this property is valid or not. }); // ┌─┐┬ ┬┌─┐┌─┐┬┌─ ┬ ┬┌─┐┬ ┬┌┬┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ // │ ├─┤├┤ │ ├┴┐ └┐┌┘├─┤│ │ ││├─┤ │ ││ ││││└─┐ // └─┘┴ ┴└─┘└─┘┴ ┴ └┘ ┴ ┴┴─┘┴─┴┘┴ ┴ ┴ ┴└─┘┘└┘└─┘ // Now loop through any validations and verify that they are appropriate for the attribute's type, // and that they are configured properly. _.each(_.keys(val.validations), function(validation) { var rule = VALIDATIONS[validation]; if (!rule) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The `' + validation + '` validation rule is not a supported validation rule.\n'+ 'Supported validation rules are: \n'+ _.keys(VALIDATIONS).reduce(function(a,v){a +=' - ' + v + '\n'; return a;},'') + '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } if (!_.contains(rule.expectedTypes, val.type)) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The `' + validation + '` validation rule does not apply to the `' + val.type + '` attribute type.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } if (!_.isFunction(rule.checkConfig)) { throw new Error('Consistency violation: Rule is missing `checkConfig` function! (Could an out-of-date dependency still be installed? (To resolve, try running `rm -rf node_modules && rm package-lock.json && npm install`.)'); } var ruleConfigError = rule.checkConfig(val.validations[validation]); if (ruleConfigError) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The configuration provided for the `' + validation + '` validation rule is invalid:\n'+ ruleConfigError + '\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } }); // ┬ ┬┌─┐┬ ┬┌┬┐┌─┐┌┬┐┌─┐ ┌─┐┬ ┬ ┬┬─┐┌─┐┬ ┌─┐┌─┐┌─┐┌─┐┌─┐┬┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ // └┐┌┘├─┤│ │ ││├─┤ │ ├┤ ├─┘│ │ │├┬┘├─┤│ ├─┤└─┐└─┐│ ││ │├─┤ │ ││ ││││└─┐ // └┘ ┴ ┴┴─┘┴─┴┘┴ ┴ ┴ └─┘ ┴ ┴─┘└─┘┴└─┴ ┴┴─┘ ┴ ┴└─┘└─┘└─┘└─┘┴┴ ┴ ┴ ┴└─┘┘└┘└─┘ // If the attribute is a plural association, make sure it doesn't have a `columnName`. if (val.collection) { if (val.columnName) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The `columnName` property is not valid for plural associations.\n'+ 'If this is a "many-to-many" association and you wish to customize the junction table,\n'+ 'use the `through` property.\n\n'+ 'See http://sailsjs.com/docs/concepts/models-and-orm/associations/through-associations\n'+ 'for more details.\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // Otherwise there's nothing more to check for plural associations. return; } // ┌┐ ┌─┐┬┬ ┌─┐┌┐┌ ┬ ┬┌┐┌┌─┐┬ ┬┌─┐┌─┐┌─┐┬─┐┌┬┐┌─┐┌┬┐ ┌┬┐┬ ┬┌─┐┌─┐┌─┐ // ├┴┐├─┤││ │ ││││ │ ││││└─┐│ │├─┘├─┘│ │├┬┘ │ ├┤ ││ │ └┬┘├─┘├┤ └─┐ // └─┘┴ ┴┴┴─┘ └─┘┘└┘ └─┘┘└┘└─┘└─┘┴ ┴ └─┘┴└─ ┴ └─┘─┴┘ ┴ ┴ ┴ └─┘└─┘ if (noLongerSupportedTypes[val.type]) { throw flaverr({ name: 'userError', code: 'E_INVALID_MODEL_DEF' }, new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In the `' + attributeName + '` attribute of model `' + modelIdentity + '`:\n'+ 'The type "' + val.type + '" is no longer supported. To use this type in your model, change\n'+ '`type` to one of the supported types and set the `columnType` property to a column \n'+ 'type supported by the model\'s adapter, e.g. { type: \'' + noLongerSupportedTypes[val.type].suggestType + '\', columnType: \'' + noLongerSupportedTypes[val.type].suggestColumnType + '\' }\n'+ '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n')); } // ┌┬┐┌─┐┌─┐ ┌┬┐┌─┐┌─┐┬─┐┌─┐┌─┐┌─┐┌┬┐┌─┐┌┬┐ ┌┬┐┬ ┬┌─┐┌─┐┌─┐ // │││├─┤├─┘ ││├┤ ├─┘├┬┘├┤ │ ├─┤ │ ├┤ ││ │ └┬┘├─┘├┤ └─┐ // ┴ ┴┴ ┴┴ ─┴┘└─┘┴ ┴└─└─┘└─┘┴ ┴ ┴ └─┘─┴┘ ┴ ┴ ┴ └─┘└─┘ // If the attribute type is no longer supported, transform it with a warning. if (typeMapping[val.type]) { sails.log.debug('In model `'+ modelIdentity + '`, the `' + attributeName + '` attribute declares deprecated type `' + val.type+'`.'); sails.log.debug('Please update to a known type (changing to `' + typeMapping[val.type] + '` for now). If you wish to use'); sails.log.debug('a specific column type supported by your database, set the `columnType` property'); sails.log.debug('(otherwise the adapter will choose an appropriate column type automatically, if relevant).\n'); val.type = typeMapping[val.type]; } // ┌─┐┌─┐┌┬┐ ┌─┐┬ ┬┌┬┐┌─┐╔╦╗┬┌─┐┬─┐┌─┐┌┬┐┬┌─┐┌┐┌┌─┐ ┬ ┬┌─┐┬ ┬ ┬┌─┐┌─┐ // └─┐├┤ │ ├─┤│ │ │ │ │║║║││ ┬├┬┘├─┤ │ ││ ││││└─┐ └┐┌┘├─┤│ │ │├┤ └─┐ // └─┘└─┘ ┴ ┴ ┴└─┘ ┴ └─┘╩ ╩┴└─┘┴└─┴ ┴ ┴ ┴└─┘┘└┘└─┘ └┘ ┴ ┴┴─┘└─┘└─┘└─┘ // Every attribute needs an `autoMigrations` dictionary val.autoMigrations = val.autoMigrations || {}; // Move certain attribute properties into `autoMigrations`. These are not valid top-level // properties as far as waterline-schema is concerned. var PROPS_TO_AUTOMIGRATE = ['autoIncrement', 'unique', 'columnType']; _.each(PROPS_TO_AUTOMIGRATE, function(property) { if (!_.isUndefined(val[property])) { val.autoMigrations[property] = val[property]; delete val[property]; } }); // Set the `unique` autoMigration property to `true` if it's the primary key, // otherwise default it to `false` if it's not already configured. val.autoMigrations.unique = val.autoMigrations.unique || (normalizedModelDef.primaryKey === attributeName) || false; // Set the `autoIncrement` autoMigration property to `false` if it's not already configured. val.autoMigrations.autoIncrement = val.autoMigrations.autoIncrement || false; // Set the `columnType` autoMigration property for non-associations. `columnType` for // singular ("model") associations will be set later, in a call to `normalizeColumnTypes`. // This lets `waterline-schema` further validate the models (e.g. verifying that associations // are valid) before we continue. if (val.type) { val.autoMigrations.columnType = val.autoMigrations.columnType || (function setColumnType() { // Primary keys get a special '_stringkey' or '_numberkey' column type. if (normalizedModelDef.primaryKey === attributeName) { return '_' + val.type.toLowerCase() + 'key'; } // Timestamps get a special '_stringtimestamp' or '_numbertimestamp' column type. if (val.autoUpdatedAt || val.autoCreatedAt) { return '_' + val.type.toLowerCase() + 'timestamp'; } // Otherwise just use the lower-cased type, prefixed with an underscore. return '_' + val.type.toLowerCase(); })(); } }); // ███╗ ██╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ██╗ ██╗███████╗███████╗ // ████╗ ██║██╔═══██╗██╔══██╗████╗ ████║██╔══██╗██║ ██║╚══███╔╝██╔════╝ // ██╔██╗ ██║██║ ██║██████╔╝██╔████╔██║███████║██║ ██║ ███╔╝ █████╗ // ██║╚██╗██║██║ ██║██╔══██╗██║╚██╔╝██║██╔══██║██║ ██║ ███╔╝ ██╔══╝ // ██║ ╚████║╚██████╔╝██║ ██║██║ ╚═╝ ██║██║ ██║███████╗██║███████╗███████╗ // ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚══════╝ // // ████████╗ ██████╗ ██████╗ ██╗ ███████╗██╗ ██╗███████╗██╗ // ╚══██╔══╝██╔═══██╗██╔══██╗ ██║ ██╔════╝██║ ██║██╔════╝██║ // ██║ ██║ ██║██████╔╝█████╗██║ █████╗ ██║ ██║█████╗ ██║ // ██║ ██║ ██║██╔═══╝ ╚════╝██║ ██╔══╝ ╚██╗ ██╔╝██╔══╝ ██║ // ██║ ╚██████╔╝██║ ███████╗███████╗ ╚████╔╝ ███████╗███████╗ // ╚═╝ ╚═════╝ ╚═╝ ╚══════╝╚══════╝ ╚═══╝ ╚══════╝╚══════╝ // // If this is production, force `migrate: safe`!! // (note that we check `sails.config.environment` and process.env.NODE_ENV // just to be on the conservative side) if ( normalizedModelDef.migrate !== 'safe' && (sails.config.environment === 'production' || process.env.NODE_ENV === 'production')) { normalizedModelDef.migrate = 'safe'; sails.log.verbose('For `%s` model, forcing Waterline to use `migrate: "safe" strategy (since this is production)', modelIdentity); } // ┌─┐┌─┐┌┬┐ ┌┬┐┬┌─┐┬─┐┌─┐┌┬┐┌─┐ ┬ ┬┌─┐┬ ┬ ┬┌─┐ // └─┐├┤ │ │││││ ┬├┬┘├─┤ │ ├┤ └┐┌┘├─┤│ │ │├┤ // └─┘└─┘ ┴ ┴ ┴┴└─┘┴└─┴ ┴ ┴ └─┘ └┘ ┴ ┴┴─┘└─┘└─┘ // Now that we have a normalized model definition, verify that a valid datastore setting is present: // (note that much of the stuff below about arrays is for backwards-compatibility) // If a datastore is not configured in our normalized model def (i.e. it is falsy), then we throw a fatal error. if (!normalizedModelDef.datastore) { throw constructError(modelHasNoDatastoreError, { modelIdentity: modelIdentity }); } // ┌┐┌┌─┐┬─┐┌┬┐┌─┐┬ ┬┌─┐┌─┐ ┌┬┐┌─┐┌┬┐┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐ ┌─┐┌─┐┌┐┌┌─┐┬┌─┐ // ││││ │├┬┘│││├─┤│ │┌─┘├┤ ││├─┤ │ ├─┤└─┐ │ │ │├┬┘├┤ │ │ ││││├┤ ││ ┬ // ┘└┘└─┘┴└─┴ ┴┴ ┴┴─┘┴└─┘└─┘ ─┴┘┴ ┴ ┴ ┴ ┴└─┘ ┴ └─┘┴└─└─┘ └─┘└─┘┘└┘└ ┴└─┘ // Make sure that `Model.datastore` is a string. if (!_.isString(normalizedModelDef.datastore)) { throw new Error( '\n-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'+ 'In model `' + modelIdentity + '`:\n'+ 'The `datastore` property must be a string representing the datastore to use for the model.\n'+ 'Instead, got: ' + util.inspect(normalizedModelDef.datastore, {depth: null}) + '\n'+ (_.isArray(normalizedModelDef.datastore) ? '(Models can only use one datastore at a time.)\n' : '') + '-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-\n'); } // Grab the normalized configuration for the datastore referenced by this model. // If the normalized model def doesn't have a `schema` flag, then check out its // normalized datastore config to see if _it_ has a `schema` setting. // // > Usually this is a default coming from the adapter itself-- for example, // > `sails-mongo` and `sails-disk` set `schema: false` by default, whereas // > `sails-mysql` and `sails-postgresql` default to `schema: true`. // > See `lib/validate-datastore-config.js` to see how that stuff gets in there. var normalizedDatastoreConfig = hook.normalizedDSConfigs[normalizedModelDef.datastore]; if (!_.isObject(normalizedDatastoreConfig)) { throw new Error('A model (`'+modelIdentity+'`) references a datastore which cannot be found (`'+normalizedModelDef.datastore+'`). If this model definition has an explicit `datastore` property, check that it is spelled correctly. If not, check your default `datastore` (usually located in `config/models.js`). Finally, check that this datastore (`'+normalizedModelDef.datastore+'`) is valid as per http://sailsjs.com/config/datastores.'); } if (_.isUndefined(normalizedModelDef.schema)) { if (!_.isUndefined(normalizedDatastoreConfig.schema)) { normalizedModelDef.schema = normalizedDatastoreConfig.schema; } } // ┌─┐┬ ┬┌─┐┌─┐┬┌─ ┌┬┐┌─┐┌┐┌┌─┐┌─┐ ╔═╗╦╔═┌─┐ // │ ├─┤├┤ │ ├┴┐ ││││ │││││ ┬│ │ ╠═╝╠╩╗└─┐ // └─┘┴ ┴└─┘└─┘┴ ┴ ┴ ┴└─┘┘└┘└─┘└─┘ ╩ ╩ ╩└─┘ // If the model's datastore is using `sails-mongo`, ensure that the primary key is set up properly. if (normalizedDatastoreConfig.adapter === 'sails-mongo') { // If this model is using the default datastore, and it's not defining its own primary key attribute, // then we'll have already logged a warning in `initialize.js` if the default primary key attribute // wasn't set up correctly. if (normalizedModelDef.datastore !== 'default' || (originalModelDef.attributes && originalModelDef.attributes[normalizedModelDef.primaryKey])) { var primaryKeyAttr = normalizedModelDef.attributes[normalizedModelDef.primaryKey]; if (primaryKeyAttr.autoIncrement || (primaryKeyAttr.type !== 'string' && normalizedModelDef.dontUseObjectIds !== true) || primaryKeyAttr.columnName !== '_id') { sails.log.debug('In model `' + modelIdentity + '`:'); sails.log.debug('The default primary key attribute (`' + normalizedModelDef.primaryKey + '`) is not set up correctly.' ); sails.log.debug('When using `sails-mongo`, primary keys MUST have `columnName: \'_id\'`,'); sails.log.debug('and must _not_ have `autoIncrement: true`.'); sails.log.debug('Also, in most cases (unless `dontUseObjectIds` has been set to `true` for the model),'); sails.log.debug('then the `type` of the primary key must also be `string`.'); sails.log.debug(); sails.log.debug('We\'ll set this up for you this time...'); sails.log.debug(); delete primaryKeyAttr.autoMigrations.autoIncrement; primaryKeyAttr.type = 'string'; primaryKeyAttr.columnName = '_id'; } } } // // ┌─┐┌┬┐┌┬┐┌─┐┌─┐┬ ┬ ┌─┐┌─┐┌┬┐┌┬┐┌─┐┌┬┐┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐ ┌┬┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐ // ├─┤ │ │ ├─┤│ ├─┤ │ ┬├┤ │ ││├─┤ │ ├─┤└─┐ │ │ │├┬┘├┤ │││├┤ │ ├─┤│ │ ││ // ┴ ┴ ┴ ┴ ┴ ┴└─┘┴ ┴ o└─┘└─┘ ┴ ─┴┘┴ ┴ ┴ ┴ ┴└─┘ ┴ └─┘┴└─└─┘ ┴ ┴└─┘ ┴ ┴ ┴└─┘─┴┘ // Attach .getDatastore() method to model. /** * WLModel.getDatastore() * * @returns {Datastore} */ normalizedModelDef.getDatastore = function (){ if (arguments.length > 0) { throw new Error('The `getDatastore()` model method should be called with no arguments. (To look up a particular datastore by name, use `sails.getDatastore()`.)'); } return hook.getDatastore(normalizedModelDef.datastore); }; // ┌─┐┌┬┐┌┬┐┌─┐┌─┐┬ ┬ ┌┐┌┌─┐┌┬┐┬┬ ┬┌─┐ ┌┬┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐ // ├─┤ │ │ ├─┤│ ├─┤ │││├─┤ │ │└┐┌┘├┤ │││├┤ │ ├─┤│ │ ││ // ┴ ┴ ┴ ┴ ┴ ┴└─┘┴ ┴ o┘└┘┴ ┴ ┴ ┴ └┘ └─┘ ┴ ┴└─┘ ┴ ┴ ┴└─┘─┴┘ // Attach .native() method to model. /** * WLModel.native() * * Obtain a raw MongoDB collection instance; the physical representation of this model. * (This is the traditional, no-longer-recommended way of performing raw Mongo queries.) * * > Provides backwards compatibility. For more info, see: * > • http://sailsjs.com/docs/reference/waterline-orm/models/native * > • https://github.com/balderdashy/sails-mongo/blob/8fcf69a2edea3977b7b094552aab45fddad3a673/lib/adapter.js#L246-L262 * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * @param {Function} * @param {Error?} err * @param {Ref} rawMongoCollection * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ normalizedModelDef.native = function (done){ // Log a compatibility warning. console.warn('\n'+ '`.native()` is deprecated. Please use `.getDatastore().manager` instead.\n'+ '(See http://sailsjs.com/upgrading)\n' ); // Ensure valid usage. if (!done) { throw new Error('No callback function provided when invoking .native().'); } else if (!_.isFunction(done)) { throw new Error('Invalid callback function provided when invoking .native().'); } // Determine if this model is using sails-mongo as its adapter. var isUsingSailsMongo = (function __get__(){ var normalizedDatastoreConfig = hook.normalizedDSConfigs[normalizedModelDef.datastore]; var adapterIdentity = hook.adapters[normalizedDatastoreConfig.adapter].identity; return adapterIdentity === 'sails-mongo'; })(); // If it is not using sails-mongo, send back an error. if (!isUsingSailsMongo) { return done(new Error('This model (`'+normalizedModelDef.identity+'`) does not appear to be using the `sails-mongo` adapter, so the `.native()` method is not available. (See http://sailsjs.com/docs/reference/waterline-orm/models/native)')); } // Sanity check: if (!normalizedModelDef.tableName || !_.isString(normalizedModelDef.tableName)) { return done(new Error('Consistency violation: Models should always have a valid `tableName` at this point, but this model (`'+normalizedModelDef.identity+'`) does not...')); } // Since the db connection manager exposed by `sails-mongo` is actually // the same as the Mongo client's `db` instance, we can treat it as such. var db = normalizedModelDef.getDatastore().manager; var rawMongoCollection = db.collection(normalizedModelDef.tableName); return done(undefined, rawMongoCollection); }; // ┌─┐┌┬┐┌┬┐┌─┐┌─┐┬ ┬ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬ ┌┬┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐ // ├─┤ │ │ ├─┤│ ├─┤ │─┼┐│ │├┤ ├┬┘└┬┘ │││├┤ │ ├─┤│ │ ││ // ┴ ┴ ┴ ┴ ┴ ┴└─┘┴ ┴ o└─┘└└─┘└─┘┴└─ ┴ ┴ ┴└─┘ ┴ ┴ ┴└─┘─┴┘ // Attach .query() method to model. /** * WLModel.query() * * Run a native SQL query. * (This is the traditional, no-longer-recommended way of performing raw SQL queries.) * * > Provides backwards compatibility. For more info, see: * > • http://sailsjs.com/docs/reference/waterline-orm/models/query * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * @param {String} sql * @param {Array} valuesToEscape * @param {Function} * @param {Error?} err * @param {Ref} rawResult * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ normalizedModelDef.query = function (sql, valuesToEscape, done){ // Log a compatibility warning. console.warn('\n'+ '`.query()` is deprecated. Please use `.getDatastore().sendNativeQuery()` instead.\n'+ '(See http://sailsjs.com/upgrading)\n' ); // Handle variadic usage: // ``` // .query('foo', function(){...}) // ``` if (arguments.length === 2 && _.isFunction(valuesToEscape)) { done = valuesToEscape; valuesToEscape = []; } // Ensure valid usage. if (!done) { throw new Error('No callback function provided when invoking .query().'); } else if (!_.isFunction(done)) { throw new Error('Invalid callback function provided when invoking .query().'); } // Call the `sendNativeQuery()` method on this model's datastore (RDI). normalizedModelDef.getDatastore().sendNativeQuery(sql, valuesToEscape, done); }; // ██████╗ ███████╗████████╗██╗ ██╗██████╗ ███╗ ██╗ // ██╔══██╗██╔════╝╚══██╔══╝██║ ██║██╔══██╗████╗ ██║ // ██████╔╝█████╗ ██║ ██║ ██║██████╔╝██╔██╗ ██║ // ██╔══██╗██╔══╝ ██║ ██║ ██║██╔══██╗██║╚██╗██║ // ██║ ██║███████╗ ██║ ╚██████╔╝██║ ██║██║ ╚████║ // ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ // Return the normalized model definition. return normalizedModelDef; };