UNPKG

breeze-sequelize

Version:
225 lines 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const breeze_client_1 = require("breeze-client"); const fs_1 = require("fs"); const path_1 = require("path"); const sequelize_1 = require("sequelize"); const utils_1 = require("./utils"); /** Maps Sequelize model definitions to Breeze metadata */ class ModelMapper { constructor(metadataStore) { /** Map from Sequelize DataTypes.DataType to Breeze DataType*/ this.dataTypeMap = { "STRING": breeze_client_1.DataType.String, "CHAR": breeze_client_1.DataType.String, "NCHAR": breeze_client_1.DataType.String, "TEXT": breeze_client_1.DataType.String, "NUMBER": breeze_client_1.DataType.Decimal, "TINYINT": breeze_client_1.DataType.Byte, "SMALLINT": breeze_client_1.DataType.Int16, "MEDIUMINT": breeze_client_1.DataType.Int32, "INTEGER": breeze_client_1.DataType.Int32, "BIGINT": breeze_client_1.DataType.Int64, "FLOAT": breeze_client_1.DataType.Single, "REAL": breeze_client_1.DataType.Single, "DOUBLE": breeze_client_1.DataType.Double, "DECIMAL": breeze_client_1.DataType.Decimal, "MONEY": breeze_client_1.DataType.Decimal, "SMALLMONEY": breeze_client_1.DataType.Decimal, "BOOLEAN": breeze_client_1.DataType.Boolean, "TIME": breeze_client_1.DataType.Time, "DATE": breeze_client_1.DataType.DateTime, "DATEONLY": breeze_client_1.DataType.DateTime, "HSTORE": breeze_client_1.DataType.Undefined, "JSON": breeze_client_1.DataType.String, "JSONB": breeze_client_1.DataType.String, "NOW": breeze_client_1.DataType.DateTimeOffset, "BLOB": breeze_client_1.DataType.Binary, "LONGBLOB": breeze_client_1.DataType.Binary, "IMAGE": breeze_client_1.DataType.Binary, "VARBINARY": breeze_client_1.DataType.Binary, "RANGE": breeze_client_1.DataType.Undefined, "UUID": breeze_client_1.DataType.Guid, "UUIDV1": breeze_client_1.DataType.Guid, "UUIDV4": breeze_client_1.DataType.Guid, "VIRTUAL": breeze_client_1.DataType.String, "ENUM": breeze_client_1.DataType.String, "ARRAY": breeze_client_1.DataType.Undefined, "GEOMETRY": breeze_client_1.DataType.String, "GEOGRAPHY": breeze_client_1.DataType.String, "CIDR": breeze_client_1.DataType.String, "INET": breeze_client_1.DataType.String, "MACADDR": breeze_client_1.DataType.String, "CITEXT": breeze_client_1.DataType.String, }; this.metadataStore = metadataStore; } /** Load the sequelize instance with the model files from the directory */ static loadSequelizeModels(sequelize, modeldir) { const initFile = path_1.join(modeldir, "init-models.js"); if (fs_1.existsSync(initFile)) { // load models via initModels function utils_1.log("loading", initFile); const init = require(initFile); init.initModels(sequelize); } else { // load models by requiring each file const files = fs_1.readdirSync(modeldir); files.forEach(file => { const modelpath = path_1.join(modeldir, file); utils_1.log("loading", modelpath); const mod = require(modelpath); if (typeof mod === "function") { mod(sequelize, sequelize_1.DataTypes); } }); } } /** Add all the Models in the Sequelize instance to the MetadataStore */ addModels(sequelize, namespace) { const models = sequelize.models; const modelNames = Object.keys(models); modelNames.forEach(name => { utils_1.log(`Adding ${name}`); const modelCtor = sequelize.model(name); this.addModel(modelCtor, namespace); }); this.addInverseNavigations(); } /** Convert the Sequelize Model to a Breeze EntityType and add it to the MetadataStore */ addModel(model, namespace) { if (model.options.schema) { namespace += "." + model.options.schema; } const pluralName = sequelize_1.Utils.pluralize(model.name); const config = { shortName: model.name, namespace: namespace, defaultResourceName: pluralName, }; const et = new breeze_client_1.EntityType(config); const attrNames = Object.keys(model.rawAttributes); const version = model.options.version === true ? "version" : model.options.version; attrNames.forEach(attrName => { const attr = model.rawAttributes[attrName]; const dataType = this.mapDataType(attr.type); if (!dataType || dataType === breeze_client_1.DataType.Undefined) { utils_1.log(`Sequelize data type ${attr.type} is not supported. Model '${model.name}', attr '${attrName}'.`); } const dp = new breeze_client_1.DataProperty({ nameOnServer: attrName, isNullable: attr.allowNull, isPartOfKey: attr.primaryKey, dataType: dataType, defaultValue: attr.defaultValue, }); if (version && attrName === version) { dp.concurrencyMode = "Fixed"; } const maxLength = this.getLength(attr.type); if (maxLength) { dp.maxLength = maxLength; } if (attr.primaryKey && attr.autoIncrement) { // Guessing about Identity vs KeyGenerator et.autoGeneratedKeyType = dataType.isInteger ? breeze_client_1.AutoGeneratedKeyType.Identity : breeze_client_1.AutoGeneratedKeyType.KeyGenerator; } if (attr.references) { // attr is a foreign key const ref = attr.references; // refName is name of referenced entity, e.g. customer const refName = (typeof ref.model === "string") ? ref.model : ref.model.name; // use the id property name to make the navigation property name let propName; if (attrName.toLowerCase().endsWith('id')) { propName = attrName.substring(0, attrName.length - 2); } else { propName = this.matchCase(refName, attrName); // if there's an existing property, add fk name to property name to make it unique if (et.getNavigationProperty(propName)) { propName = attr.field + '_' + propName; } } const np = new breeze_client_1.NavigationProperty({ associationName: (model.name + '_' + propName + '_' + attrName).toLocaleLowerCase(), entityTypeName: refName, foreignKeyNamesOnServer: [attrName], isScalar: true, nameOnServer: propName }); et.addProperty(np); } et.addProperty(dp); }); this.metadataStore.addEntityType(et); } /** Create non-scalar navigation properties on the inverse side of the scalar properties, * for all EntityTypes in the MetadataStore. */ addInverseNavigations() { const ms = this.metadataStore; const entityTypes = this.metadataStore.getEntityTypes().filter(t => !t.isComplexType); entityTypes.forEach(entityType => { const navs = entityType.getProperties().filter(p => p.isNavigationProperty && p.isScalar); navs.forEach(nav => { // nav is e.g. Order.Customer property // inverseName is e.g. "Orders" let inverseName = this.matchCase(sequelize_1.Utils.pluralize(entityType.shortName), nav.nameOnServer); // navType is e.g. Customer const navType = ms.getAsEntityType(nav.entityTypeName); // inverseNav is e.g. Customer.Orders const inverseNav = navType.getNavigationProperty(inverseName); if (inverseNav == null || inverseNav.associationName !== nav.associationName) { if (inverseNav != null) { // already a property with this name, so add uniquifier e.g. Customer_Orders inverseName = nav.name + '_' + inverseName; } const np = new breeze_client_1.NavigationProperty({ associationName: nav.associationName, entityTypeName: entityType.name, invForeignKeyNames: nav.foreignKeyNames, isScalar: false, nameOnServer: inverseName }); navType.addProperty(np); } }); }); } /** Make the first char of s match the case of the first char of the target string */ matchCase(s, target) { if (/^[A-Z]/.test(target)) { s = s[0].toUpperCase() + s.substring(1); } else { s = s[0].toLowerCase() + s.substring(1); } return s; } /** Return the Breeze DataType for the given Sequelize DataType */ mapDataType(sqDataType) { let name = (typeof sqDataType === "string") ? sqDataType : sqDataType.key; let type = this.dataTypeMap[name]; if (!type) { // if type is e.g. NCHAR(5), try without the (5) const p = name.indexOf('('); if (p > 0) { name = name.substring(0, p); type = this.dataTypeMap[name]; } } return type; } /** For string data types, return the length, else undefined */ getLength(sqDataType) { if (typeof sqDataType === "string" || (sqDataType.key !== "STRING" && sqDataType.key !== "CHAR")) { return undefined; } const stringType = sqDataType; return stringType.options && stringType.options.length; } } exports.ModelMapper = ModelMapper; //# sourceMappingURL=ModelMapper.js.map