Skip to content

Commit

Permalink
Convert to TypeScript, for maintainability and type definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
steveschmitt committed Jan 23, 2020
1 parent e339a2b commit f2bda31
Show file tree
Hide file tree
Showing 34 changed files with 2,102 additions and 56 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
*.csproj
*.sln
*.log
*.tgz
.vscode

node_modules
bin
Expand Down
21 changes: 21 additions & 0 deletions breeze-sequelize/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# breeze-sequelize

The `breeze-sequelize` library lets you easily build a Sequelize server for managing relational data.
Starting with Breeze metadata, it will create the Sequelize model for you, and Sequelize can create a database from the model.

Once you have the model and database, `breeze-sequelize` makes it easy to query and update data from your Breeze client.

## Repo

The [src](./src) folder contains the TypeScript source code.

The [test](./test) folder contains the unit tests (work in progress).

The [test/ExpressDemo](./test/ExpressDemo) folder contains a complete server using breeze-sequelize, for running an end-to-end test suite.

## Documentation

[Breeze/Sequelize documentation here](http://breeze.github.io/doc-node-sequelize/ "breeze-sequelize documentation")

[Learn more about Breeze](http://breeze.github.io/doc-js/ "breezejs").

File renamed without changes.
17 changes: 17 additions & 0 deletions breeze-sequelize/old/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# The "breeze-sequelize" npm package

This is the official NPM package for the Breeze Sequelize integration. The package files are in the [breeze.server.node](https://github.com/Breeze/breeze.server.node "github: "breeze-server-node") repository in the 'breeze-sequelize' subfolder.

To install with npm, open a terminal or command window and enter:

`npm install breeze-sequelize`

>Case matters! Be sure to spell "breeze-sequelize" in all lowercase.
[Learn more about Breeze](http://breeze.github.io/doc-js/ "breezejs").

## More documentation

[Breeze/Sequelize documentation here](http://breeze.github.io/doc-node-sequelize/ "breeze-sequelize documentation")


File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions breeze-sequelize/src/main.js → breeze-sequelize/old/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ exports.dbUtils = require("./dbUtils");
exports.breeze = require("breeze-client"); // needed because we have augmented breeze in the SequelizeQuery component

exports.Sequelize = exports.SequelizeManager.Sequelize;

exports.ModelMapper = require("./ModelMapper.js");
47 changes: 47 additions & 0 deletions breeze-sequelize/old/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "breeze-sequelize",
"version": "0.3.0",
"description": "Breeze Sequelize server implementation",
"keywords": [
"breeze",
"sequelize",
"orm",
"query",
"linq",
"graph"
],
"main": "main.js",
"directories": {},
"dependencies": {
"bluebird": "^3.5.5",
"breeze-client": "^2.0.2",
"lodash": "^4.17.15",
"sequelize": "^5.21.3",
"toposort": "^2.0.2"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^6.2.0"
},
"scripts": {
"test": "mocha"
},
"repository": {
"type": "git",
"url": "https://github.com/Breeze/breeze.server.node.git"
},
"homepage": "http://breeze.github.io/doc-node-sequelize/introduction.html",
"bugs": "https://github.com/Breeze/breeze.server.node/issues",
"author": {
"name": "IdeaBlade",
"email": "[email protected]",
"url": "https://www.ideablade.com/"
},
"contributors": [
"Jay Traband",
"Steve Schmitt",
"Marcel Good",
"Ward Bell"
],
"license": "MIT"
}
File renamed without changes.
File renamed without changes.
172 changes: 172 additions & 0 deletions breeze-sequelize/src/MetadataMapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { breeze, ComplexType, DataProperty, EntityType, MetadataStore, NavigationProperty, StructuralType } from "breeze-client";
import { AbstractDataType, DataTypes, Model, ModelAttributeColumnOptions, ModelAttributes, ModelOptions, Sequelize } from "sequelize";
import * as _ from 'lodash';
import * as utils from "./utils";

let log = utils.log;

export interface NameModelMap { [modelName: string]: { new(): Model } & typeof Model };

// TODO: still need to handle inherited entity types - TPT
/** Maps Breeze metadata to Sequelize Models */
export class MetadataMapper {
sequelize: Sequelize
metadataStore: MetadataStore;
entityTypeSqModelMap: NameModelMap;
resourceNameSqModelMap: NameModelMap;

constructor(breezeMetadata: MetadataStore | string | Object, sequelize: Sequelize) {
this.sequelize = sequelize;
let ms;
if (breezeMetadata instanceof MetadataStore) {
ms = breezeMetadata;
} else {
ms = new breeze.MetadataStore();
ms.importMetadata(breezeMetadata);
}

this.metadataStore = ms;
this._createMaps();
}

/** creates entityTypeSqModelMap and resourceNameSqModelMap */
private _createMaps() {

let ms = this.metadataStore;
let allTypes = ms.getEntityTypes();
let typeMap = _.groupBy(allTypes, t => {
return t.isComplexType ? "complexType" : "entityType";
});
// let complexTypes = typeMap["complexType"];
let entityTypes = typeMap["entityType"];

// map of entityTypeName to sqModel
let entityTypeSqModelMap: NameModelMap = this.entityTypeSqModelMap = {};
// first create all of the sequelize types with just data properties
entityTypes.forEach(entityType => {
let typeConfig = this.mapToSqModelConfig(entityType);
let options: ModelOptions = {
// NOTE: case sensitivity of the table name may not be the same on some sql databases.
modelName: entityType.shortName, // this will define the table's name; see options.define
};
let sqModel = this.sequelize.define(entityType.shortName, typeConfig, options) as { new(): Model } & typeof Model;
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 */
private createNavProps(entityTypes: StructuralType[], entityTypeSqModelMap: NameModelMap) {
// TODO: we only support single column foreignKeys for now.

entityTypes.forEach(entityType => {
let navProps = entityType.navigationProperties as NavigationProperty[];
let sqModel = entityTypeSqModelMap[entityType.name];
navProps.forEach(np => {
let npName = np.nameOnServer;

let targetEntityType = np.entityType;
let 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 */
private mapToSqModelConfig(entityOrComplexType: StructuralType): ModelAttributes {
// propConfig looks like
// { firstProp: { type: Sequelize.XXX, ... },
// secondProp: { type: Sequelize.XXX, ... }
// ..
// }

let typeConfig = {} as ModelAttributes;
entityOrComplexType.dataProperties.forEach(dataProperty => {
let propConfig = this.mapToSqPropConfig(dataProperty);
_.merge(typeConfig, propConfig);
});

return typeConfig;
}

/** Creates Sequelize column attributes based on a DataProperty */
private mapToSqPropConfig(dataProperty: DataProperty): ModelAttributes {
if (dataProperty.isComplexProperty) {
return this.mapToSqModelConfig(dataProperty.dataType as ComplexType);
}
let propConfig = {} as ModelAttributes;
let attributes = {} as ModelAttributeColumnOptions;
propConfig[dataProperty.nameOnServer] = attributes;
let sqModel = _dataTypeMap[dataProperty.dataType.name];
if (sqModel == null) {
let 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.DataType.String && dataProperty.maxLength) {
attributes.type = DataTypes.STRING(dataProperty.maxLength);
}
if (!dataProperty.isNullable) {
attributes.allowNull = false;
}
if (dataProperty.isPartOfKey) {
attributes.primaryKey = true;
if ((dataProperty.parentType as EntityType).autoGeneratedKeyType == breeze.AutoGeneratedKeyType.Identity) {
let dt = attributes.type as AbstractDataType;
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;
}
}


let _dataTypeMap = {
String: DataTypes.STRING,
Boolean: DataTypes.BOOLEAN,
DateTime: DataTypes.DATE,
DateTimeOffset: DataTypes.DATE,
Byte: DataTypes.INTEGER.UNSIGNED,
Int16: DataTypes.INTEGER,
Int32: DataTypes.INTEGER,
Int64: DataTypes.BIGINT,
Decimal: DataTypes.DECIMAL(19, 4),
Double: DataTypes.FLOAT,
Single: DataTypes.FLOAT,
Guid: DataTypes.UUID,
Binary: DataTypes.STRING().BINARY,
Time: DataTypes.STRING,
Undefined: DataTypes.BLOB
};
9 changes: 8 additions & 1 deletion breeze-sequelize/src/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# The "breeze-sequelize" npm package
# breeze-sequelize

This is the official NPM package for the Breeze Sequelize integration. The package files are in the [breeze.server.node](https://github.com/Breeze/breeze.server.node "github: "breeze-server-node") repository in the 'breeze-sequelize' subfolder.

The `breeze-sequelize` library lets you easily build a Sequelize server for managing relational data.
Starting with Breeze metadata, it will create the Sequelize model for you, and Sequelize can create a database from the model.

Once you have the model and database, `breeze-sequelize` makes it easy to query and update data from your Breeze client.

## Install

To install with npm, open a terminal or command window and enter:

`npm install breeze-sequelize`
Expand Down
Loading

0 comments on commit f2bda31

Please sign in to comment.