breeze-sequelize
Version:
Breeze Sequelize server implementation
168 lines • 7.96 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const breeze_client_1 = require("breeze-client");
const sequelize_1 = require("sequelize");
const _ = require("lodash");
const utils = require("./utils");
const log = utils.log;
// TODO: still need to handle inherited entity types - TPT
/** Maps Breeze metadata to Sequelize Models */
class MetadataMapper {
constructor(breezeMetadata, sequelize) {
this.sequelize = sequelize;
let ms;
if (breezeMetadata instanceof breeze_client_1.MetadataStore) {
ms = breezeMetadata;
}
else {
ms = new breeze_client_1.breeze.MetadataStore();
ms.importMetadata(breezeMetadata);
}
this.metadataStore = ms;
this._createMaps();
}
/** creates entityTypeSqModelMap and resourceNameSqModelMap */
_createMaps() {
const ms = this.metadataStore;
const allTypes = ms.getEntityTypes();
const typeMap = _.groupBy(allTypes, t => {
return t.isComplexType ? "complexType" : "entityType";
});
// let complexTypes = typeMap["complexType"];
const entityTypes = typeMap["entityType"];
// map of entityTypeName to sqModel
const entityTypeSqModelMap = this.entityTypeSqModelMap = {};
// first create all of the sequelize types with just data properties
entityTypes.forEach(entityType => {
const typeConfig = this.mapToSqModelConfig(entityType);
const modelName = entityType.shortName;
// find model in sequelize instance, else create model
let sqModel;
if (this.sequelize.isDefined(modelName)) {
log("model already defined: ", modelName);
sqModel = this.sequelize.model(modelName);
}
else {
const options = {
// NOTE: case sensitivity of the table name may not be the same on some sql databases.
modelName: modelName,
};
log("model not defined: ", modelName);
sqModel = this.sequelize.define(modelName, typeConfig, options);
}
entityTypeSqModelMap[entityType.name] = sqModel;
}, this);
// now add navigation props
this.createNavProps(entityTypes, entityTypeSqModelMap);
// map of breeze resourceName to sequelize model
this.resourceNameSqModelMap = _.mapValues(ms._resourceEntityTypeMap, (value, key) => {
return entityTypeSqModelMap[value];
});
}
// source.fn(target, { foreignKey: })
// hasOne - adds a foreign key to target
// belongsTo - add a foreign key to source
// hasMany - adds a foreign key to target, unless you also specifiy that target hasMany source, in which case a junction table is created with sourceId and targetId
/** Adds relationships to the Models based on Breeze NavigationProperties */
createNavProps(entityTypes, entityTypeSqModelMap) {
// TODO: we only support single column foreignKeys for now.
entityTypes.forEach(entityType => {
const navProps = entityType.navigationProperties;
const sqModel = entityTypeSqModelMap[entityType.name];
navProps.forEach(np => {
const npName = np.nameOnServer;
if (sqModel.associations[npName]) {
// already mapped
return;
}
const targetEntityType = np.entityType;
const targetSqModel = entityTypeSqModelMap[targetEntityType.name];
if (np.isScalar) {
if (np.foreignKeyNamesOnServer.length > 0) {
sqModel.belongsTo(targetSqModel, { as: npName, foreignKey: np.foreignKeyNamesOnServer[0], onDelete: "no action" }); // Product, Category
}
else {
sqModel.hasOne(targetSqModel, { as: npName, foreignKey: np.invForeignKeyNamesOnServer[0], onDelete: "no action" }); // Order, InternationalOrder
}
}
else {
if (np.foreignKeyNamesOnServer.length > 0) {
throw new Error("not sure what kind of reln this is");
// sqModel.hasMany(targetSqModel, { as: npName, foreignKey: np.foreignKeyNamesOnServer[0]})
}
else {
sqModel.hasMany(targetSqModel, { as: npName, foreignKey: np.invForeignKeyNamesOnServer[0], onDelete: "no action" }); // Category, Product
}
}
});
});
}
/** Creates a set of Sequelize attributes based on DataProperties */
mapToSqModelConfig(entityOrComplexType) {
// propConfig looks like
// { firstProp: { type: Sequelize.XXX, ... },
// secondProp: { type: Sequelize.XXX, ... }
// ..
// }
const typeConfig = {};
entityOrComplexType.dataProperties.forEach(dataProperty => {
const propConfig = this.mapToSqPropConfig(dataProperty);
_.merge(typeConfig, propConfig);
});
return typeConfig;
}
/** Creates Sequelize column attributes based on a DataProperty */
mapToSqPropConfig(dataProperty) {
if (dataProperty.isComplexProperty) {
return this.mapToSqModelConfig(dataProperty.dataType);
}
const propConfig = {};
const attributes = {};
propConfig[dataProperty.nameOnServer] = attributes;
const sqModel = _dataTypeMap[dataProperty.dataType.name];
if (sqModel == null) {
const template = _.template("Unable to map the dataType '${ dataType }' of dataProperty: '${ dataProperty }'");
throw new Error(template({ dataProperty: dataProperty.parentType.shortName + "." + dataProperty.name, dataType: dataProperty.dataType.name }));
}
attributes.type = sqModel;
if (dataProperty.dataType === breeze_client_1.breeze.DataType.String && dataProperty.maxLength) {
attributes.type = sequelize_1.DataTypes.STRING(dataProperty.maxLength);
}
if (!dataProperty.isNullable) {
attributes.allowNull = false;
}
if (dataProperty.isPartOfKey) {
attributes.primaryKey = true;
if (dataProperty.parentType.autoGeneratedKeyType === breeze_client_1.breeze.AutoGeneratedKeyType.Identity) {
const dt = attributes.type;
if (dt.key === "INTEGER" || dt.key === "BIGINT") {
attributes.autoIncrement = true;
}
}
}
if (dataProperty.defaultValue !== undefined && !dataProperty.isPartOfKey) {
// if (dataProperty.defaultValue !== undefined) {
attributes.defaultValue = dataProperty.defaultValue;
}
return propConfig;
}
}
exports.MetadataMapper = MetadataMapper;
const _dataTypeMap = {
String: sequelize_1.DataTypes.STRING,
Boolean: sequelize_1.DataTypes.BOOLEAN,
DateTime: sequelize_1.DataTypes.DATE,
DateTimeOffset: sequelize_1.DataTypes.DATE,
Byte: sequelize_1.DataTypes.TINYINT.UNSIGNED,
Int16: sequelize_1.DataTypes.SMALLINT,
Int32: sequelize_1.DataTypes.INTEGER,
Int64: sequelize_1.DataTypes.BIGINT,
Decimal: sequelize_1.DataTypes.DECIMAL(19, 4),
Double: sequelize_1.DataTypes.DOUBLE,
Single: sequelize_1.DataTypes.FLOAT,
Guid: sequelize_1.DataTypes.UUID,
Binary: sequelize_1.DataTypes.STRING().BINARY,
Time: sequelize_1.DataTypes.STRING,
Undefined: sequelize_1.DataTypes.BLOB
};
//# sourceMappingURL=MetadataMapper.js.map