Skip to content

Commit c569be6

Browse files
Merge pull request #186 from RobinBuschmann/issue-#122
Issue #122
2 parents 581fd57 + 3532589 commit c569be6

38 files changed

+884
-456
lines changed

.gitignore

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@
88
# libraries
99
node_modules
1010

11-
# testing
12-
coverage
13-
.nyc_output
14-
1511
#logs
1612
npm-debug.log
1713

14+
# build
1815
**/*.js
1916
**/*.js.map
2017
**/*.d.ts
18+
19+
# src
2120
!lib/models/Model.d.ts
2221
!lib/models/Model.js
2322
!lib/models/Sequelize.d.ts
2423
!lib/models/Sequelize.js
2524

25+
# testing
26+
coverage
27+
.nyc_output
28+
!test/tsconfig.mocha.js

example/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Book from "./models/Book";
1010
/* tslint:disable:no-console */
1111
/* tslint:disable:no-unused-new */
1212

13-
new Sequelize({
13+
const s = new Sequelize({
1414
validateOnly: true,
1515
modelPaths: [__dirname + '/models/validation-only']
1616
});

index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ export {IScopeFindOptions} from "./lib/interfaces/IScopeFindOptions";
9898
export {IScopeIncludeAssociation} from "./lib/interfaces/IScopeIncludeAssociation";
9999
export {IScopeIncludeOptions} from "./lib/interfaces/IScopeIncludeOptions";
100100
export {IScopeOptions} from "./lib/interfaces/IScopeOptions";
101-
export {ISequelizeAssociation} from "./lib/interfaces/ISequelizeAssociation";
102101
export {ISequelizeConfig} from "./lib/interfaces/ISequelizeConfig";
103102
export {ISequelizeForeignKeyConfig} from "./lib/interfaces/ISequelizeForeignKeyConfig";
104103
export {ISequelizeValidationOnlyConfig} from "./lib/interfaces/ISequelizeValidationOnlyConfig";

lib/annotations/Column.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function annotate(target: any,
4040
};
4141
} else {
4242

43-
options = Object.assign({}, <IPartialDefineAttributeColumnOptions>optionsOrDataType);
43+
options = Object.assign({}, optionsOrDataType as IPartialDefineAttributeColumnOptions);
4444

4545
if (!options.type) {
4646
options.type = getSequelizeTypeByDesignType(target, propertyName);
Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import {AssociationOptionsBelongsTo} from 'sequelize';
2-
import {BELONGS_TO, addAssociation} from "../../services/association";
2+
import {addAssociation, getPreparedAssociationOptions} from "../../services/association";
33
import {ModelClassGetter} from "../../types/ModelClassGetter";
4+
import {BelongsToAssociation} from '../../models/association/BelongsToAssociation';
45

5-
export function BelongsTo(relatedClassGetter: ModelClassGetter,
6+
export function BelongsTo(associatedClassGetter: ModelClassGetter,
67
foreignKey?: string): Function;
7-
export function BelongsTo(relatedClassGetter: ModelClassGetter,
8+
export function BelongsTo(associatedClassGetter: ModelClassGetter,
89
options?: AssociationOptionsBelongsTo): Function;
9-
export function BelongsTo(relatedClassGetter: ModelClassGetter,
10+
export function BelongsTo(associatedClassGetter: ModelClassGetter,
1011
optionsOrForeignKey?: string | AssociationOptionsBelongsTo): Function {
1112

1213
return (target: any, propertyName: string) => {
13-
addAssociation(
14-
target,
15-
BELONGS_TO,
16-
relatedClassGetter,
17-
propertyName,
18-
optionsOrForeignKey
14+
const options: AssociationOptionsBelongsTo = getPreparedAssociationOptions(optionsOrForeignKey);
15+
if (!options.as) options.as = propertyName;
16+
addAssociation(target, new BelongsToAssociation(
17+
associatedClassGetter,
18+
options,
19+
)
1920
);
2021
};
2122
}
Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,35 @@
1-
import {BELONGS_TO_MANY, addAssociation} from "../../services/association";
1+
import {addAssociation} from "../../services/association";
22
import {ModelClassGetter} from "../../types/ModelClassGetter";
33
import {IAssociationOptionsBelongsToMany} from "../../interfaces/IAssociationOptionsBelongsToMany";
4+
import {BelongsToManyAssociation} from '../../models/association/BelongsToManyAssociation';
45

5-
export function BelongsToMany(relatedClassGetter: ModelClassGetter,
6-
through: (ModelClassGetter) | string,
6+
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
7+
through: ModelClassGetter | string,
78
foreignKey?: string,
89
otherKey?: string): Function;
9-
export function BelongsToMany(relatedClassGetter: ModelClassGetter,
10+
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
1011
options: IAssociationOptionsBelongsToMany): Function;
11-
export function BelongsToMany(relatedClassGetter: ModelClassGetter,
12-
throughOrOptions: (ModelClassGetter | string) | IAssociationOptionsBelongsToMany,
12+
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
13+
throughOrOptions: ModelClassGetter | string | IAssociationOptionsBelongsToMany,
1314
foreignKey?: string,
1415
otherKey?: string): Function {
15-
const typeOfThroughOrOptions = typeof throughOrOptions;
16-
let through;
17-
let options: Partial<IAssociationOptionsBelongsToMany>;
1816

19-
if (typeOfThroughOrOptions === 'string' || typeOfThroughOrOptions === 'function') {
20-
through = throughOrOptions;
21-
} else {
22-
through = (throughOrOptions as IAssociationOptionsBelongsToMany).through;
23-
options = throughOrOptions as IAssociationOptionsBelongsToMany;
24-
}
2517
return (target: any, propertyName: string) => {
26-
addAssociation(
27-
target,
28-
BELONGS_TO_MANY,
29-
relatedClassGetter,
30-
propertyName,
31-
options || foreignKey,
32-
through,
33-
otherKey,
18+
let options: Partial<IAssociationOptionsBelongsToMany> = {foreignKey, otherKey};
19+
20+
if (typeof throughOrOptions === 'string' ||
21+
typeof throughOrOptions === 'function') {
22+
options.through = throughOrOptions;
23+
} else {
24+
options = {...throughOrOptions};
25+
}
26+
27+
if (!options.as) options.as = propertyName;
28+
29+
addAssociation(target, new BelongsToManyAssociation(
30+
associatedClassGetter,
31+
options as IAssociationOptionsBelongsToMany,
32+
)
3433
);
3534
};
3635
}
Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import {AssociationOptionsHasMany} from 'sequelize';
2-
import {HAS_MANY, addAssociation} from "../../services/association";
2+
import {addAssociation, getPreparedAssociationOptions} from "../../services/association";
3+
import {Association} from "../../enums/Association";
34
import {ModelClassGetter} from "../../types/ModelClassGetter";
5+
import {HasAssociation} from '../../models/association/HasAssociation';
46

5-
export function HasMany(relatedClassGetter: ModelClassGetter,
7+
export function HasMany(associatedClassGetter: ModelClassGetter,
68
foreignKey?: string): Function;
7-
export function HasMany(relatedClassGetter: ModelClassGetter,
9+
export function HasMany(associatedClassGetter: ModelClassGetter,
810
options?: AssociationOptionsHasMany): Function;
9-
export function HasMany(relatedClassGetter: ModelClassGetter,
11+
export function HasMany(associatedClassGetter: ModelClassGetter,
1012
optionsOrForeignKey?: string | AssociationOptionsHasMany): Function {
13+
1114
return (target: any, propertyName: string) => {
12-
addAssociation(
13-
target,
14-
HAS_MANY,
15-
relatedClassGetter,
16-
propertyName,
17-
optionsOrForeignKey,
15+
const options: AssociationOptionsHasMany = getPreparedAssociationOptions(optionsOrForeignKey);
16+
if (!options.as) options.as = propertyName;
17+
addAssociation(target, new HasAssociation(
18+
associatedClassGetter,
19+
options,
20+
Association.HasMany,
21+
)
1822
);
1923
};
2024
}

lib/annotations/association/HasOne.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import {AssociationOptionsHasOne} from 'sequelize';
2-
import {addAssociation, HAS_ONE} from "../../services/association";
2+
import {Association} from "../../enums/Association";
33
import {ModelClassGetter} from "../../types/ModelClassGetter";
4+
import {addAssociation, getPreparedAssociationOptions} from '../../services/association';
5+
import {HasAssociation} from '../../models/association/HasAssociation';
46

5-
export function HasOne(relatedClassGetter: ModelClassGetter,
7+
export function HasOne(associatedClassGetter: ModelClassGetter,
68
foreignKey?: string): Function;
7-
export function HasOne(relatedClassGetter: ModelClassGetter,
9+
export function HasOne(associatedClassGetter: ModelClassGetter,
810
options?: AssociationOptionsHasOne): Function;
9-
export function HasOne(relatedClassGetter: ModelClassGetter,
11+
export function HasOne(associatedClassGetter: ModelClassGetter,
1012
optionsOrForeignKey?: string | AssociationOptionsHasOne): Function {
13+
1114
return (target: any, propertyName: string) => {
12-
addAssociation(
13-
target,
14-
HAS_ONE,
15-
relatedClassGetter,
16-
propertyName,
17-
optionsOrForeignKey,
15+
const options: AssociationOptionsHasOne = getPreparedAssociationOptions(optionsOrForeignKey);
16+
if (!options.as) options.as = propertyName;
17+
addAssociation(target, new HasAssociation(
18+
associatedClassGetter,
19+
options,
20+
Association.HasOne,
21+
)
1822
);
1923
};
2024
}

lib/enums/Association.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export enum Association {
2+
BelongsToMany = 'belongsToMany',
3+
BelongsTo = 'belongsTo',
4+
HasMany = 'hasMany',
5+
HasOne = 'hasOne',
6+
}

lib/interfaces/AssociationOptions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {
2+
AssociationOptionsBelongsTo, AssociationOptionsHasMany,
3+
AssociationOptionsHasOne, AssociationOptionsManyToMany
4+
} from 'sequelize';
5+
import {IPreparedAssociationOptionsBelongsToMany} from './IPreparedAssociationOptionsBelongsToMany';
6+
7+
export type AssociationOptions =
8+
AssociationOptionsBelongsTo |
9+
IPreparedAssociationOptionsBelongsToMany |
10+
AssociationOptionsHasMany |
11+
AssociationOptionsHasOne |
12+
AssociationOptionsManyToMany;
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {AssociationForeignKeyOptions, AssociationOptionsManyToMany} from "sequelize";
22
import {ModelClassGetter} from "../types/ModelClassGetter";
3+
import {IThroughOptions} from './IThroughOptions';
34

45
export interface IAssociationOptionsBelongsToMany extends AssociationOptionsManyToMany {
5-
through: ModelClassGetter | string;
6+
through: ModelClassGetter | string | IThroughOptions;
67
otherKey?: string | AssociationForeignKeyOptions;
78
}

lib/interfaces/IFindOptions.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import {WhereOptions, LoggingOptions, SearchPathOptions, col, FindOptionsAttributesArray,
2-
literal, fn} from 'sequelize';
1+
import {
2+
WhereOptions, LoggingOptions, SearchPathOptions, col, FindOptionsAttributesArray,
3+
literal, fn, and, or
4+
} from 'sequelize';
35
import {Model} from "../models/Model";
46
import {IIncludeOptions} from "./IIncludeOptions";
57

@@ -11,7 +13,7 @@ export interface IFindOptions<T> extends LoggingOptions, SearchPathOptions {
1113
/**
1214
* A hash of attributes to describe your search. See above for examples.
1315
*/
14-
where?: WhereOptions<T>;
16+
where?: WhereOptions<T> | fn | or | Array<col | and | or | string>;
1517

1618
/**
1719
* A list of the attributes that you want to select. To rename an attribute, you can pass an array, with
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {AssociationForeignKeyOptions, AssociationOptionsManyToMany} from "sequelize";
2+
import {IPreparedThroughOptions} from './IPreparedThroughOptions';
3+
4+
export interface IPreparedAssociationOptionsBelongsToMany extends AssociationOptionsManyToMany {
5+
through: IPreparedThroughOptions;
6+
otherKey?: string | AssociationForeignKeyOptions;
7+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {AssociationScope} from 'sequelize';
2+
import {Model} from '../models/Model';
3+
4+
/**
5+
* Used for a association table in n:m associations.
6+
*
7+
* @see AssociationOptionsBelongsToMany
8+
*/
9+
export interface IPreparedThroughOptions {
10+
11+
/**
12+
* The model used to join both sides of the N:M association.
13+
*/
14+
model: typeof Model;
15+
16+
/**
17+
* A key/value set that will be used for association create and find defaults on the through model.
18+
* (Remember to add the attributes to the through model)
19+
*/
20+
scope?: AssociationScope;
21+
22+
/**
23+
* If true a unique key will be generated from the foreign keys used (might want to turn this off and create
24+
* specific unique keys when using scopes)
25+
*
26+
* Defaults to true
27+
*/
28+
unique?: boolean;
29+
30+
}

lib/interfaces/ISequelizeAssociation.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

lib/interfaces/IThroughOptions.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {AssociationScope} from 'sequelize';
2+
import {ModelClassGetter} from '../types/ModelClassGetter';
3+
4+
/**
5+
* Used for a association table in n:m associations.
6+
*
7+
* @see AssociationOptionsBelongsToMany
8+
*/
9+
export interface IThroughOptions {
10+
11+
/**
12+
* The model used to join both sides of the N:M association.
13+
*/
14+
model: ModelClassGetter | string;
15+
16+
/**
17+
* A key/value set that will be used for association create and find defaults on the through model.
18+
* (Remember to add the attributes to the through model)
19+
*/
20+
scope?: AssociationScope;
21+
22+
/**
23+
* If true a unique key will be generated from the foreign keys used (might want to turn this off and create
24+
* specific unique keys when using scopes)
25+
*
26+
* Defaults to true
27+
*/
28+
unique?: boolean;
29+
30+
}

lib/models/BaseSequelize.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Model} from "./Model";
22
import {DEFAULT_DEFINE_OPTIONS, getModels} from "../services/models";
3-
import {getAssociations, processAssociation} from "../services/association";
3+
import {getAssociations} from "../services/association";
44
import {ISequelizeConfig} from "../interfaces/ISequelizeConfig";
55
import {ISequelizeUriConfig} from "../interfaces/ISequelizeUriConfig";
66
import {ISequelizeDbNameConfig} from "../interfaces/ISequelizeDbNameConfig";
@@ -9,7 +9,7 @@ import {resolveScopes} from "../services/scopes";
99
import {installHooks} from "../services/hooks";
1010
import {ISequelizeValidationOnlyConfig} from "../interfaces/ISequelizeValidationOnlyConfig";
1111
import {extend} from "../utils/object";
12-
import {ISequelizeAssociation} from "../interfaces/ISequelizeAssociation";
12+
import {BaseAssociation} from './association/BaseAssociation';
1313

1414
/**
1515
* Why does v3/Sequlize and v4/Sequelize does not extend? Because of
@@ -99,7 +99,14 @@ export abstract class BaseSequelize {
9999

100100
if (!associations) return;
101101

102-
associations.forEach(association => processAssociation(this, model, association));
102+
associations.forEach(association => {
103+
association.init(model, this);
104+
const associatedClass = association.getAssociatedClass();
105+
const relation = association.getAssociation();
106+
const options = association.getSequelizeOptions();
107+
model[relation](associatedClass, options);
108+
this.adjustAssociation(model, association);
109+
});
103110
});
104111
}
105112

@@ -110,7 +117,7 @@ export abstract class BaseSequelize {
110117
*/
111118
abstract getThroughModel(through: string): typeof Model;
112119

113-
abstract adjustAssociation(model: any, association: ISequelizeAssociation): void;
120+
abstract adjustAssociation(model: any, association: BaseAssociation): void;
114121

115122
abstract defineModels(models: Array<typeof Model>): void;
116123

0 commit comments

Comments
 (0)