UNPKG

water-orm

Version:

A monolith version of Standalone waterline ORM

295 lines (228 loc) 7.2 kB
/** * Module dependencies */ var _ = require('lodash'); var utils = require('./utils'); var hop = utils.object.hasOwnProperty; /** * Expose Attributes */ module.exports = Attributes; /** * Build an Attributes Definition * * Takes a collection of attributes from a Waterline Collection * and builds up an initial schema by normalizing into a known format. * * @param {Array} collections * @param {Object} connections * @return {Object} * @api private */ function Attributes(collections, connections, defaults) { var self = this; this.attributes = {}; // Ensure a value is set for connections connections = connections || {}; collections.forEach(function (collection) { collection = self.normalize(collection.prototype, connections, defaults); var conns = _.cloneDeep(collection.connection); var attributes = _.cloneDeep(collection.attributes); self.stripFunctions(attributes); self.stripProperties(attributes); self.validatePropertyNames(attributes); self.attributes[collection.identity.toLowerCase()] = { connection: conns, identity: collection.identity.toLowerCase(), tableName: collection.tableName || collection.identity, migrate: collection.migrate || 'safe', attributes: attributes, meta: collection.meta || {} }; }); return this.attributes; } /** * Normalize attributes for a collection into a known format. * * @param {Object} collection * @param {Object} connections * @return {Object} * @api private */ Attributes.prototype.normalize = function(collection, connections, defaults) { this.normalizeIdentity(collection); this.setDefaults(collection, defaults); this.autoAttributes(collection, connections); return collection; }; /** * Set Default Values for the collection. * * Adds flags to the collection to determine if timestamps and a primary key * should be added to the collection's schema. * * @param {Object} collection * @api private */ Attributes.prototype.setDefaults = function(collection, defaults) { // Ensure defaults is always set to something defaults = defaults || {}; if(!hop(collection, 'connection')) { collection.connection = ''; } if(!hop(collection, 'attributes')) { collection.attributes = {}; } var defaultSettings = { autoPK: true, autoCreatedAt: true, autoUpdatedAt: true, migrate: 'alter' }; // Override default settings with user defined defaults if(hop(defaults, 'autoPK')) defaultSettings.autoPK = defaults.autoPK; if(hop(defaults, 'autoCreatedAt')) defaultSettings.autoCreatedAt = defaults.autoCreatedAt; if(hop(defaults, 'autoUpdatedAt')) defaultSettings.autoUpdatedAt = defaults.autoUpdatedAt; if(hop(defaults, 'migrate')) defaultSettings.migrate = defaults.migrate; // Override defaults with collection defined values if(hop(collection, 'autoPK')) defaultSettings.autoPK = collection.autoPK; if(hop(collection, 'autoCreatedAt')) defaultSettings.autoCreatedAt = collection.autoCreatedAt; if(hop(collection, 'autoUpdatedAt')) defaultSettings.autoUpdatedAt = collection.autoUpdatedAt; if(hop(collection, 'migrate')) defaultSettings.migrate = collection.migrate; var flags = { autoPK: defaultSettings.autoPK, autoCreatedAt: defaultSettings.autoCreatedAt, autoUpdatedAt: defaultSettings.autoUpdatedAt, migrate: defaultSettings.migrate }; // Set the property for the automatic attributes if the user didn't // customize it/disable it. if(flags.autoCreatedAt === true) { flags.autoCreatedAt = 'createdAt'; } if(flags.autoUpdatedAt === true) { flags.autoUpdatedAt = 'updatedAt'; } for(var flag in flags) { collection[flag] = flags[flag]; } }; /** * Normalize identity * * @param {Object} collection * @api private */ Attributes.prototype.normalizeIdentity = function(collection) { if(hop(collection, 'tableName') && !hop(collection, 'identity')) { collection.identity = collection.tableName.toLowerCase(); } // Require an identity so the object key can be set if(!hop(collection, 'identity')) { throw new Error('A Collection must include an identity or tableName attribute'); } }; /** * Add Auto Attribute definitions to the schema if they are not defined. * * Adds in things such as an Id primary key and timestamps unless they have been * disabled in the collection. * * @param {Object} collection * @param {Object} connections * @api private */ Attributes.prototype.autoAttributes = function(collection, connections) { var attributes = collection.attributes; var pk = false; var mainConnection; // Check to make sure another property hasn't set itself as a primary key for(var key in attributes) { if(hop(attributes[key], 'primaryKey')) pk = true; } // If a primary key was manually defined, turn off autoPK if(pk) collection.autoPK = false; // Add a primary key attribute if(!pk && collection.autoPK && !attributes.id) { attributes.id = { type: 'integer', autoIncrement: true, primaryKey: true, unique: true }; // Check if the adapter used in the collection specifies the primary key format if(Array.isArray(collection.connection)) { mainConnection = collection.connection[0]; } else { mainConnection = collection.connection; } if(hop(connections, mainConnection)) { var connection = connections[mainConnection]; if(hop(connection._adapter, 'pkFormat')) { attributes.id.type = connection._adapter.pkFormat; } } } // Extend definition with autoUpdatedAt and autoCreatedAt timestamps var now = { type: 'datetime', 'default': 'NOW' }; if(collection.autoCreatedAt && !attributes[collection.autoCreatedAt]) { attributes[collection.autoCreatedAt] = now; } if(collection.autoUpdatedAt && !attributes[collection.autoUpdatedAt]) { attributes[collection.autoUpdatedAt] = now; } }; /** * Strip Functions From Schema * * @param {Object} attributes * @api private */ Attributes.prototype.stripFunctions = function(attributes) { for(var attribute in attributes) { if(typeof attributes[attribute] === 'function') delete attributes[attribute]; } }; /** * Strip Non-Reserved Properties * * @param {Object} attributes * @api private */ Attributes.prototype.stripProperties = function(attributes) { for(var attribute in attributes) { this.stripProperty(attributes[attribute]); } }; /** * Strip Property that isn't in the reserved words list. * * @param {Object} * @api private */ Attributes.prototype.stripProperty = function(properties) { for(var prop in properties) { if(utils.reservedWords.indexOf(prop) > -1) continue; delete properties[prop]; } }; /** * Validates property names to ensure they are valid. * * @param {Object} * @api private */ Attributes.prototype.validatePropertyNames = function(attributes) { for(var attribute in attributes) { // Check for dots in name if(attribute.match(/\./g)) { var error = 'Invalid Attribute Name: Attributes may not contain a "."" character'; throw new Error(error); } } };