diff --git a/breeze-sequelize/src/MetadataMapper.ts b/breeze-sequelize/src/MetadataMapper.ts index 605a7d1..5a0b061 100644 --- a/breeze-sequelize/src/MetadataMapper.ts +++ b/breeze-sequelize/src/MetadataMapper.ts @@ -5,14 +5,17 @@ import * as utils from "./utils"; let log = utils.log; +/** Map name to Sequelize Model type */ 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; + readonly sequelize: Sequelize + readonly metadataStore: MetadataStore; + /** Maps entity type name to Sequelize Model */ entityTypeSqModelMap: NameModelMap; + /** Maps resource name to Sequelize Model */ resourceNameSqModelMap: NameModelMap; constructor(breezeMetadata: MetadataStore | string | Object, sequelize: Sequelize) { diff --git a/breeze-sequelize/src/SQVisitor.ts b/breeze-sequelize/src/SQVisitor.ts index 28a495a..373a9c5 100644 --- a/breeze-sequelize/src/SQVisitor.ts +++ b/breeze-sequelize/src/SQVisitor.ts @@ -5,6 +5,7 @@ import { FindOptions, IncludeOptions, LogicType, Op, Sequelize, WhereOptions } f import { Where } from "sequelize/types/lib/utils"; import { SqVisitContext } from "./SequelizeQuery"; +/** Result of processing a query expression into Sequelize */ export interface ExprResult { include: IncludeOptions[], lastInclude: IncludeOptions, diff --git a/breeze-sequelize/src/SequelizeManager.ts b/breeze-sequelize/src/SequelizeManager.ts index d6e4820..71d8ae2 100644 --- a/breeze-sequelize/src/SequelizeManager.ts +++ b/breeze-sequelize/src/SequelizeManager.ts @@ -17,10 +17,14 @@ export class SequelizeManager { sequelizeOptions: Options; dbConfig: DbConfig; sequelize: Sequelize; + /** Same as resourceNameSqModelMap */ models: NameModelMap; + /** Maps resource name to Sequelize Model */ resourceNameSqModelMap: NameModelMap; + /** Maps entity type name to Sequelize Model */ entityTypeSqModelMap: NameModelMap; metadataStore: MetadataStore; + /** Generates keys for entity types where autoGeneratedKeyType = "KeyGenerator" */ keyGenerator: KeyGenerator; constructor(dbConfig: DbConfig, sequelizeOptions: Options) { diff --git a/breeze-sequelize/src/SequelizeQuery.ts b/breeze-sequelize/src/SequelizeQuery.ts index 0939872..e628030 100644 --- a/breeze-sequelize/src/SequelizeQuery.ts +++ b/breeze-sequelize/src/SequelizeQuery.ts @@ -5,9 +5,11 @@ import { SequelizeManager } from "./SequelizeManager"; import * as urlUtils from "url"; import { toSQVisitor } from "./SQVisitor"; -// patch Breeze EntityQuery for server-side use -// TODO make this a method on SequelizeQuery, so we don't have to patch Breeze? -EntityQuery['fromUrl'] = function (url: string, resourceName: string) { +/** Create an EntityQuery from a JSON-format breeze query string + * @param url - url containing query, e.g. `/orders?{freight:{">":100}}` + * @param resourceName - Name of the resource/entity. If omitted, resourceName is derived from the pathname of the url. +*/ +export function entityQueryFromUrl(url: string, resourceName?: string): EntityQuery { let parsedUrl = urlUtils.parse(url, true); resourceName = resourceName || parsedUrl.pathname; // this is because everything after the '?' is turned into a query object with a single key @@ -23,14 +25,19 @@ EntityQuery['fromUrl'] = function (url: string, resourceName: string) { // for debugging entityQuery['jsonQuery'] = jsonQuery; return entityQuery; -} +} + +// patch Breeze EntityQuery for server-side use +// TODO make this a method on SequelizeQuery, so we don't have to patch Breeze? +EntityQuery['fromUrl'] = entityQueryFromUrl; export interface SequelizeQueryOptions { useTransaction: boolean; beforeQueryEntities: (sq: SequelizeQuery) => void; } -interface CountModel { +/** Object returned from a query with inlineCountEnabled */ +export interface CountModel { rows: Model[]; count: number; } diff --git a/breeze-sequelize/src/SequelizeSaveHandler.ts b/breeze-sequelize/src/SequelizeSaveHandler.ts index faafe7e..dbc0f14 100644 --- a/breeze-sequelize/src/SequelizeSaveHandler.ts +++ b/breeze-sequelize/src/SequelizeSaveHandler.ts @@ -6,9 +6,9 @@ import { Model, Transaction } from "sequelize"; import { KeyGenerator, SequelizeManager } from "./SequelizeManager"; let toposort = require("toposort") as (ar: any[]) => any[]; - +/** Save bundle from breeze client */ export interface SaveRequest { - body: { entities: Entity[], saveOptions: SaveOptions } + body: { entities: Entity[], saveOptions?: SaveOptions } } interface EntityGroup { @@ -18,15 +18,18 @@ interface EntityGroup { /** Handles saving entities from Breeze SaveChanges requests */ export class SequelizeSaveHandler { - sequelizeManager: SequelizeManager; - metadataStore: MetadataStore; - entitiesFromClient: Entity[]; + readonly sequelizeManager: SequelizeManager; + readonly metadataStore: MetadataStore; + readonly entitiesFromClient: Entity[]; saveOptions: SaveOptions; private _keyMappings: KeyMapping[]; private _fkFixupMap: { [entityKeyName: string]: any }; private _savedEntities: Entity[]; + /** Generates keys for entity types where autoGeneratedKeyType = "KeyGenerator" */ keyGenerator: KeyGenerator; - beforeSaveEntity: (ei: EntityInfo) => EntityInfo; + /** Process an entity before save. If false is returned, entity is not saved. */ + beforeSaveEntity: (ei: EntityInfo) => boolean; + /** Process all entities before save. The entities in the returned SaveMap are saved. */ beforeSaveEntities: (sm: SaveMap, trx?: Transaction) => SaveMap; /** Create an instance for the given save request */ @@ -44,7 +47,7 @@ export class SequelizeSaveHandler { } - /** Save the entities in the save request */ + /** Save the entities in the save request, returning either the saved entities or an error collection */ save(): Promise<{ errors: ServerEntityError[], message: string }> | Promise { let beforeSaveEntity = (this.beforeSaveEntity || noopBeforeSaveEntity).bind(this); let entityTypeMap = {}; diff --git a/breeze-sequelize/src/dbUtils.ts b/breeze-sequelize/src/dbUtils.ts index 63d4144..4eeb171 100644 --- a/breeze-sequelize/src/dbUtils.ts +++ b/breeze-sequelize/src/dbUtils.ts @@ -6,13 +6,15 @@ let log = utils.log; exports.connect = connect; exports.createDb = createDb; +/** Config for connecting to a database */ export interface DbConfig { dbName: string; user: string; password: string; } -/** @returns Promise<"success"> or throws an error */ +/** Connect to existing database. + * @returns Promise<"success"> or throws an error */ export function connect(dbConfig: DbConfig, sequelizeOptions: Options): Promise { let sequelize = new Sequelize(dbConfig.dbName, dbConfig.user, dbConfig.password, sequelizeOptions); @@ -26,7 +28,8 @@ export function connect(dbConfig: DbConfig, sequelizeOptions: Options): Promise< }); }; -/** @returns Promise or throws an error */ +/** Create new database. + * @returns Promise or throws an error */ export function createDb(dbConfig: DbConfig, sequelizeOptions: Options): Promise { let sequelize = new Sequelize(null, dbConfig.user, dbConfig.password, sequelizeOptions); let statement = 'CREATE DATABASE ' + dbConfig.dbName; diff --git a/breeze-sequelize/src/main.ts b/breeze-sequelize/src/main.ts index 8e5c65d..544c476 100644 --- a/breeze-sequelize/src/main.ts +++ b/breeze-sequelize/src/main.ts @@ -1,4 +1,4 @@ -import { SequelizeQuery } from "./SequelizeQuery"; +import { SequelizeQuery, entityQueryFromUrl } from "./SequelizeQuery"; import { SequelizeManager } from "./SequelizeManager"; import { SequelizeSaveHandler } from "./SequelizeSaveHandler"; import * as utils from "./utils"; @@ -6,4 +6,4 @@ import * as dbUtils from "./dbUtils"; import { breeze } from "breeze-client"; const Sequelize = SequelizeManager.Sequelize; -export { SequelizeQuery, SequelizeManager, Sequelize, SequelizeSaveHandler, utils, dbUtils, breeze } +export { SequelizeQuery, entityQueryFromUrl, SequelizeManager, Sequelize, SequelizeSaveHandler, utils, dbUtils, breeze } diff --git a/breeze-sequelize/src/package.json b/breeze-sequelize/src/package.json index 6fc3c9c..7f91a1b 100644 --- a/breeze-sequelize/src/package.json +++ b/breeze-sequelize/src/package.json @@ -1,6 +1,6 @@ { "name": "breeze-sequelize", - "version": "0.4.0", + "version": "0.4.1", "description": "Breeze Sequelize server implementation", "keywords": [ "breeze", @@ -19,21 +19,23 @@ ], "directories": {}, "dependencies": { + "toposort": "^2.0.2" + }, + "peerDependencies": { "bluebird": "^3.7.2", - "breeze-client": ">=2.0.3", + "breeze-client": ">=2.0.4", "lodash": "^4.17.15", - "sequelize": "^5.21.3", - "toposort": "^2.0.2" + "sequelize": "^5.21.3" }, "devDependencies": { "@types/bluebird": "^3.5.29", "@types/lodash": "^4.14.137", - "@types/node": "^12.12.25", + "@types/node": "^12.12.26", "@types/validator": "^12.0.1", "chai": "^4.2.0", "mocha": "^6.2.0", - "rimraf": "^3.0.0", - "typescript": "~3.4.5" + "rimraf": "^3.0.1", + "typescript": "^3.7.5" }, "scripts": { "test": "mocha", diff --git a/breeze-sequelize/src/utils.ts b/breeze-sequelize/src/utils.ts index 3179088..1c998c0 100644 --- a/breeze-sequelize/src/utils.ts +++ b/breeze-sequelize/src/utils.ts @@ -1,4 +1,4 @@ - +/** Wrapper around console.log. Use `log.enabled` to enable/disable */ export function log(s: any, ...args: any[]) { if (!log['enabled']) return; console.log('[Breeze] ' + s + '\n', args); diff --git a/breeze-sequelize/test/package.json b/breeze-sequelize/test/package.json index 467ba44..2074827 100644 --- a/breeze-sequelize/test/package.json +++ b/breeze-sequelize/test/package.json @@ -6,11 +6,12 @@ "directories": {}, "dependencies": { "bluebird": "^3.7.2", - "breeze-client": ">=2.0.3", - "breeze-sequelize": ">=0.4.0", + "breeze-client": ">=2.0.4", + "breeze-sequelize": "^0.4.1", "lodash": "^4.17.15", "mysql": "^2.18.0", "mysql2": "^1.7.0", + "sequelize": "^5.21.3", "uuid": "^3.4.0" }, "devDependencies": { @@ -21,6 +22,7 @@ "scripts": { "test": "mocha", "debug": "node --inspect-brk node_modules/mocha/bin/mocha -b", + "install-lib": "npm install ../src/breeze-sequelize-0.4.1.tgz", "gen-model": "sequelize-auto -o \"./models\" -d northwindib -h localhost -u mysql -p 3306 -x mysql -e mysql" }, "repository": {