Skip to content

Commit

Permalink
BREAKING CHANGE!: Upgrade to Sequelize 6
Browse files Browse the repository at this point in the history
The breakage comes from following Sequelize's lead and requiring a minimum node version of 10 and switching out Bluebird for native promises.
  • Loading branch information
kf6kjg committed May 19, 2020
1 parent 883cb2c commit ebb6ba2
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 592 deletions.
669 changes: 143 additions & 526 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"glob": "7.1.2"
},
"devDependencies": {
"@types/bluebird": "3.5.25",
"@types/chai": "3.4.35",
"@types/chai-as-promised": "0.0.29",
"@types/chai-datetime": "0.0.30",
Expand All @@ -61,25 +60,24 @@
"nyc": "13.3.0",
"prettyjson": "1.2.1",
"reflect-metadata": "0.1.9",
"sequelize": "5.15.1",
"sequelize": "^6.0.0-beta.6",
"sinon": "1.17.7",
"sinon-chai": "2.8.0",
"source-map-support": "0.4.14",
"sqlite3": "4.0.4",
"sqlite3": "4.2.0",
"ts-node": "7.0.1",
"tslint": "5.14.0",
"typescript": "3.3.3",
"uuid-validate": "0.0.2"
},
"peerDependencies": {
"@types/bluebird": "*",
"@types/node": "*",
"@types/validator": "*",
"reflect-metadata": "*",
"sequelize": "^5.1.0"
"sequelize": "^6.0.0-beta.6"
},
"engines": {
"node": ">=0.8.15"
"node": ">=10.0.0"
},
"nyc": {
"lines": 85,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class BelongsToManyAssociation extends BaseAssociation {
const associatedClass = this.getAssociatedClass();
const throughOptions = this.getThroughOptions(sequelize);

const throughModel = typeof throughOptions === 'object' ? throughOptions.model : undefined;
const throughModel = typeof throughOptions === 'object' && typeof throughOptions.model !== "string" ? throughOptions.model : undefined;
options.through = throughOptions;
options.foreignKey = getForeignKeyOptions(model, throughModel, this.options.foreignKey);
options.otherKey = getForeignKeyOptions(associatedClass, throughModel, this.options.otherKey);
Expand Down
16 changes: 8 additions & 8 deletions src/model/model/model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {InitOptions, Model as OriginModel, ModelAttributes, FindOptions, BuildOptions, Promise} from 'sequelize';
import {InitOptions, Model as OriginModel, ModelAttributes, FindOptions, BuildOptions} from 'sequelize';
import {capitalize} from '../../shared/string';
import {inferAlias} from '../../associations/alias-inference/alias-inference-service';
import {ModelNotInitializedError} from '../shared/model-not-initialized-error';
Expand All @@ -16,15 +16,15 @@ export type $GetType<T> = NonNullable<T> extends any[] ? NonNullable<T> : (NonNu
export abstract class Model<T = any, T2 = any> extends OriginModel<T, T2> {

// TODO Consider moving the following props to OriginModel
id?: number | any;
createdAt?: Date | any;
updatedAt?: Date | any;
deletedAt?: Date | any;
version?: number | any;
public id?: number | any;
public createdAt?: Date | any;
public updatedAt?: Date | any;
public deletedAt?: Date | any;
public version?: number | any;

static isInitialized = false;

static init(attributes: ModelAttributes, options: InitOptions): void {
static init(attributes: ModelAttributes, options: InitOptions): Model {
this.isInitialized = true;
// @ts-ignore
return super.init(attributes, options);
Expand Down Expand Up @@ -135,7 +135,7 @@ function isFunctionMember(propertyKey: string, target: any): boolean {

function isForbiddenMember(propertyKey: string): boolean {
const FORBIDDEN_KEYS = ['name', 'constructor', 'length', 'prototype', 'caller', 'arguments', 'apply',
'QueryInterface', 'QueryGenerator', 'init', 'replaceHookAliases', 'refreshAttributes', 'inspect'];
'queryInterface', 'queryGenerator', 'init', 'replaceHookAliases', 'refreshAttributes', 'inspect'];
return FORBIDDEN_KEYS.indexOf(propertyKey) !== -1;
}

Expand Down
1 change: 1 addition & 0 deletions src/model/shared/model-service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "reflect-metadata";
import {DataTypeAbstract, ModelOptions} from 'sequelize';

import {Model} from '../model/model';
Expand Down
4 changes: 2 additions & 2 deletions src/sequelize/sequelize/sequelize-options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Options} from "sequelize";
import {ModelCtor} from "../../model/model/model";
import {Model, ModelCtor} from "../../model/model/model";

export type ModelMatch = (filename: string, member: string) => boolean;

Expand All @@ -9,7 +9,7 @@ export interface SequelizeOptions extends Options {
* Path to models or actual models,
* which should be loaded for sequelize instance
*/
models?: string[] | ModelCtor[];
models?: string[] | Array<ModelCtor<Model>>;

/**
* Path to models, which should be loaded
Expand Down
9 changes: 5 additions & 4 deletions src/sequelize/sequelize/sequelize-service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {basename, extname, join, parse} from "path";

import * as glob from "glob";
import {uniqueFilter} from "../../shared/array";
import {ModelMatch, SequelizeOptions} from "./sequelize-options";
import {ModelCtor} from "../../model/model/model";
import {Model, ModelCtor} from "../../model/model/model";

/**
* Prepares sequelize config passed to original sequelize constructor
Expand Down Expand Up @@ -37,9 +38,9 @@ function getValidationOnlyOptions(options: SequelizeOptions): SequelizeOptions {
* Determines models from value
*/
export function getModels(
arg: Array<ModelCtor | string>,
arg: Array<ModelCtor<Model> | string>,
modelMatch: ModelMatch,
): ModelCtor[] {
): Array<ModelCtor<Model>> {
const hasSupportedExtension = path => ['.ts', '.js'].indexOf(extname(path)) !== -1;

if (arg && typeof arg[0] === 'string') {
Expand Down Expand Up @@ -74,7 +75,7 @@ export function getModels(
;
}

return arg as ModelCtor[];
return arg as Array<ModelCtor<Model>>;
}

/**
Expand Down
21 changes: 10 additions & 11 deletions src/sequelize/sequelize/sequelize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,18 @@ export class Sequelize extends OriginSequelize {
}
}

model(model: string | typeof Model): ModelCtor {
model(model: string | typeof Model): ModelCtor<Model> {
if (typeof model !== 'string') {
return super.model(getModelName(model.prototype)) as ModelCtor;
return super.model(getModelName(model.prototype)) as ModelCtor<Model>;
}
return super.model(model) as ModelCtor;
return super.model(model) as ModelCtor<Model>;
}

addModels(models: ModelCtor[]);
addModels(models: Array<ModelCtor<Model>>);
addModels(modelPaths: string[]);
addModels(modelPaths: string[], modelMatch?: ModelMatch);
addModels(arg: Array<ModelCtor | string>);
addModels(arg: Array<ModelCtor | string>, modelMatch?: ModelMatch) {
addModels(arg: Array<ModelCtor<Model> | string>);
addModels(arg: Array<ModelCtor<Model> | string>, modelMatch?: ModelMatch) {
const defaultModelMatch = (filename, member) => filename === member;
const models = getModels(arg, modelMatch || this.options.modelMatch || defaultModelMatch);

Expand All @@ -58,7 +58,7 @@ export class Sequelize extends OriginSequelize {
return this.model(modelClass as any) as Repository<M>;
}

private associateModels(models: ModelCtor[]): void {
private associateModels(models: Array<ModelCtor<Model>>): void {

models.forEach(model => {
const associations = getAssociations(model.prototype);
Expand All @@ -69,7 +69,7 @@ export class Sequelize extends OriginSequelize {
const options = association.getSequelizeOptions(model, this);
const associatedClass = this.model(association.getAssociatedClass());

if (!associatedClass.isInitialized) {
if (!associatedClass.sequelize) {
throw new ModelNotInitializedError(
associatedClass,
`Association between ${associatedClass.name} and ${model.name} cannot be resolved.`
Expand All @@ -80,8 +80,7 @@ export class Sequelize extends OriginSequelize {
});
}

private defineModels(models: ModelCtor[]): ModelCtor[] {

private defineModels(models: Array<ModelCtor<Model>>): Array<ModelCtor<Model>> {
return models.map(model => {
const modelName = getModelName(model.prototype);
const attributes = getAttributes(model.prototype);
Expand Down Expand Up @@ -109,7 +108,7 @@ export class Sequelize extends OriginSequelize {
});
}

private createRepositoryModel(modelClass: ModelCtor): ModelCtor {
private createRepositoryModel(modelClass: ModelCtor<Model>): ModelCtor<Model> {
return class extends modelClass<any> {
};
}
Expand Down
1 change: 0 additions & 1 deletion test/specs/association.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import {expect, use} from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as OriginSequelize from 'sequelize';
import * as Promise from 'bluebird';
import {createSequelize} from "../utils/sequelize";
import {
Sequelize, Model, Table, Column, BelongsToMany,
Expand Down
1 change: 0 additions & 1 deletion test/specs/instance-methods.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {expect} from 'chai';
import * as Promise from 'bluebird';
import {Model, Table, Column} from "../../src";
import {createSequelize} from "../utils/sequelize";

Expand Down
52 changes: 24 additions & 28 deletions test/specs/instance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as Promise from 'bluebird';
import { expect, use } from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import { useFakeTimers } from 'sinon';
Expand Down Expand Up @@ -654,9 +653,10 @@ describe('instance', () => {
it('should support updating a subset of attributes', () =>
User
.create({aNumber: 1, bNumber: 1})
// TODO Sequelize typings issue caused by sequelize/types/lib/model.d.ts on line 2394
// TODO The order of overloads is wrong
.tap((user) => User.update({bNumber: 2}, {where: {id: user.get('id') as any}}))
.then((user) => {
User.update({bNumber: 2}, {where: {id: user.get('id') }});
return user;
})
.then((user) => user.reload({attributes: ['bNumber']}))
.then((user) => {
expect(user.get('aNumber')).to.equal(1);
Expand Down Expand Up @@ -777,7 +777,7 @@ describe('instance', () => {
})
.then((lePlayer: Player) => {
expect(lePlayer.shoe).not.to.be.null;
return lePlayer.shoe.destroy().return(lePlayer);
return lePlayer.shoe.destroy().then(() => lePlayer);
})
.then((lePlayer) => lePlayer.reload() as any)
.then((lePlayer: Player) => {
Expand Down Expand Up @@ -809,7 +809,8 @@ describe('instance', () => {
.destroy()
.then(() => {
return leTeam.players[0].destroy();
}).return(leTeam);
})
.then(() => leTeam);
})
.then((leTeam) => leTeam.reload() as any)
.then((leTeam: Team) => {
Expand Down Expand Up @@ -1321,7 +1322,7 @@ describe('instance', () => {
.update(
{aNumber: 1},
{
where: {},
where: {},
// TODO silent options missing in UpdateOptions
['silent' as any]: true
}
Expand Down Expand Up @@ -1496,7 +1497,7 @@ describe('instance', () => {
describe('with version option', () => {

it("version column is updated by sequelize", () => {
let version = undefined;
let version: number | any;
UserWithCustomUpdatedAt
.sync()
.then(() => UserWithVersion.create({name: 'john doe'}))
Expand Down Expand Up @@ -1666,15 +1667,15 @@ describe('instance', () => {
it('saves many objects that each a have collection of eagerly loaded objects', () =>

Promise
.props({
bart: UserEager.create({username: 'bart', age: 20}),
lisa: UserEager.create({username: 'lisa', age: 20}),
detention1: ProjectEager.create({title: 'detention1', overdueDays: 0}),
detention2: ProjectEager.create({title: 'detention2', overdueDays: 0}),
exam1: ProjectEager.create({title: 'exam1', overdueDays: 0}),
exam2: ProjectEager.create({title: 'exam2', overdueDays: 0})
})
.then(({bart, lisa, detention1, detention2, exam1, exam2}) =>
.all([
UserEager.create({username: 'bart', age: 20}),
UserEager.create({username: 'lisa', age: 20}),
ProjectEager.create({title: 'detention1', overdueDays: 0}),
ProjectEager.create({title: 'detention2', overdueDays: 0}),
ProjectEager.create({title: 'exam1', overdueDays: 0}),
ProjectEager.create({title: 'exam2', overdueDays: 0})
])
.then(([bart, lisa, detention1, detention2, exam1, exam2]) =>
Promise
.all([
bart.$set('projects', [detention1, detention2]),
Expand All @@ -1686,13 +1687,10 @@ describe('instance', () => {
include: [ProjectEager]
}))
.then((simpsons) => {
let _bart;
let _lisa;

expect(simpsons.length).to.equal(2);

_bart = simpsons[0];
_lisa = simpsons[1];
const _bart = simpsons[0];
const _lisa = simpsons[1];

expect(_bart.projects).to.exist;
expect(_lisa.projects).to.exist;
Expand Down Expand Up @@ -2412,12 +2410,10 @@ describe('instance', () => {

describe('restore', () => {

it('returns an error if the model is not paranoid', () =>

User.create({username: 'Peter'}).then((user) =>
expect(() => user.restore()).to.throw(Error, 'Model is not paranoid')
)
);
it('returns an error if the model is not paranoid', async () => {
const user = await User.create({username: 'Peter'});
return expect(user.restore()).to.be.rejectedWith(Error, 'Model is not paranoid');
});

it('restores a previously deleted model', () => {

Expand Down
1 change: 0 additions & 1 deletion test/specs/model-methods.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {expect} from 'chai';
import * as Promise from 'bluebird';
import {Model, Table, Column} from "../../src";
import {createSequelize} from "../utils/sequelize";

Expand Down
5 changes: 2 additions & 3 deletions test/specs/model.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {useFakeTimers, stub, spy} from 'sinon';
import * as sinonChai from 'sinon-chai';
import * as _ from 'lodash';
import * as moment from 'moment';
import * as Promise from 'bluebird';
import {Op, UniqueConstraintError} from 'sequelize';
import * as chaiAsPromised from 'chai-as-promised';
import {createSequelize} from "../utils/sequelize";
Expand Down Expand Up @@ -453,7 +452,7 @@ describe('model', () => {
return Promise.all([
User.create({username: 'tobi', email: '[email protected]'}),
User.create({username: 'tobi', email: '[email protected]'})]);
}).catch(UniqueConstraintError, (err) => {
}).catch((err) => {
expect(err.message).to.equal('User and email must be unique');
});
});
Expand Down Expand Up @@ -509,7 +508,7 @@ describe('model', () => {
return Promise.all([
User.create({userId: 1, email: '[email protected]'}),
User.create({userId: 1, email: '[email protected]'})]);
}).catch(UniqueConstraintError, (err) => {
}).catch((err) => {
expect(err.message).to.equal('User and email must be unique');
});
});
Expand Down

0 comments on commit ebb6ba2

Please sign in to comment.