Skip to content

Commit

Permalink
Merge pull request #186 from RobinBuschmann/issue-#122
Browse files Browse the repository at this point in the history
Issue #122
  • Loading branch information
RobinBuschmann committed Nov 4, 2017
2 parents 581fd57 + 3532589 commit c569be6
Show file tree
Hide file tree
Showing 38 changed files with 884 additions and 456 deletions.
11 changes: 7 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@
# libraries
node_modules

# testing
coverage
.nyc_output

#logs
npm-debug.log

# build
**/*.js
**/*.js.map
**/*.d.ts

# src
!lib/models/Model.d.ts
!lib/models/Model.js
!lib/models/Sequelize.d.ts
!lib/models/Sequelize.js

# testing
coverage
.nyc_output
!test/tsconfig.mocha.js
2 changes: 1 addition & 1 deletion example/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Book from "./models/Book";
/* tslint:disable:no-console */
/* tslint:disable:no-unused-new */

new Sequelize({
const s = new Sequelize({
validateOnly: true,
modelPaths: [__dirname + '/models/validation-only']
});
Expand Down
1 change: 0 additions & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ export {IScopeFindOptions} from "./lib/interfaces/IScopeFindOptions";
export {IScopeIncludeAssociation} from "./lib/interfaces/IScopeIncludeAssociation";
export {IScopeIncludeOptions} from "./lib/interfaces/IScopeIncludeOptions";
export {IScopeOptions} from "./lib/interfaces/IScopeOptions";
export {ISequelizeAssociation} from "./lib/interfaces/ISequelizeAssociation";
export {ISequelizeConfig} from "./lib/interfaces/ISequelizeConfig";
export {ISequelizeForeignKeyConfig} from "./lib/interfaces/ISequelizeForeignKeyConfig";
export {ISequelizeValidationOnlyConfig} from "./lib/interfaces/ISequelizeValidationOnlyConfig";
Expand Down
2 changes: 1 addition & 1 deletion lib/annotations/Column.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function annotate(target: any,
};
} else {

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

if (!options.type) {
options.type = getSequelizeTypeByDesignType(target, propertyName);
Expand Down
21 changes: 11 additions & 10 deletions lib/annotations/association/BelongsTo.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import {AssociationOptionsBelongsTo} from 'sequelize';
import {BELONGS_TO, addAssociation} from "../../services/association";
import {addAssociation, getPreparedAssociationOptions} from "../../services/association";
import {ModelClassGetter} from "../../types/ModelClassGetter";
import {BelongsToAssociation} from '../../models/association/BelongsToAssociation';

export function BelongsTo(relatedClassGetter: ModelClassGetter,
export function BelongsTo(associatedClassGetter: ModelClassGetter,
foreignKey?: string): Function;
export function BelongsTo(relatedClassGetter: ModelClassGetter,
export function BelongsTo(associatedClassGetter: ModelClassGetter,
options?: AssociationOptionsBelongsTo): Function;
export function BelongsTo(relatedClassGetter: ModelClassGetter,
export function BelongsTo(associatedClassGetter: ModelClassGetter,
optionsOrForeignKey?: string | AssociationOptionsBelongsTo): Function {

return (target: any, propertyName: string) => {
addAssociation(
target,
BELONGS_TO,
relatedClassGetter,
propertyName,
optionsOrForeignKey
const options: AssociationOptionsBelongsTo = getPreparedAssociationOptions(optionsOrForeignKey);
if (!options.as) options.as = propertyName;
addAssociation(target, new BelongsToAssociation(
associatedClassGetter,
options,
)
);
};
}
45 changes: 22 additions & 23 deletions lib/annotations/association/BelongsToMany.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
import {BELONGS_TO_MANY, addAssociation} from "../../services/association";
import {addAssociation} from "../../services/association";
import {ModelClassGetter} from "../../types/ModelClassGetter";
import {IAssociationOptionsBelongsToMany} from "../../interfaces/IAssociationOptionsBelongsToMany";
import {BelongsToManyAssociation} from '../../models/association/BelongsToManyAssociation';

export function BelongsToMany(relatedClassGetter: ModelClassGetter,
through: (ModelClassGetter) | string,
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
through: ModelClassGetter | string,
foreignKey?: string,
otherKey?: string): Function;
export function BelongsToMany(relatedClassGetter: ModelClassGetter,
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
options: IAssociationOptionsBelongsToMany): Function;
export function BelongsToMany(relatedClassGetter: ModelClassGetter,
throughOrOptions: (ModelClassGetter | string) | IAssociationOptionsBelongsToMany,
export function BelongsToMany(associatedClassGetter: ModelClassGetter,
throughOrOptions: ModelClassGetter | string | IAssociationOptionsBelongsToMany,
foreignKey?: string,
otherKey?: string): Function {
const typeOfThroughOrOptions = typeof throughOrOptions;
let through;
let options: Partial<IAssociationOptionsBelongsToMany>;

if (typeOfThroughOrOptions === 'string' || typeOfThroughOrOptions === 'function') {
through = throughOrOptions;
} else {
through = (throughOrOptions as IAssociationOptionsBelongsToMany).through;
options = throughOrOptions as IAssociationOptionsBelongsToMany;
}
return (target: any, propertyName: string) => {
addAssociation(
target,
BELONGS_TO_MANY,
relatedClassGetter,
propertyName,
options || foreignKey,
through,
otherKey,
let options: Partial<IAssociationOptionsBelongsToMany> = {foreignKey, otherKey};

if (typeof throughOrOptions === 'string' ||
typeof throughOrOptions === 'function') {
options.through = throughOrOptions;
} else {
options = {...throughOrOptions};
}

if (!options.as) options.as = propertyName;

addAssociation(target, new BelongsToManyAssociation(
associatedClassGetter,
options as IAssociationOptionsBelongsToMany,
)
);
};
}
24 changes: 14 additions & 10 deletions lib/annotations/association/HasMany.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import {AssociationOptionsHasMany} from 'sequelize';
import {HAS_MANY, addAssociation} from "../../services/association";
import {addAssociation, getPreparedAssociationOptions} from "../../services/association";
import {Association} from "../../enums/Association";
import {ModelClassGetter} from "../../types/ModelClassGetter";
import {HasAssociation} from '../../models/association/HasAssociation';

export function HasMany(relatedClassGetter: ModelClassGetter,
export function HasMany(associatedClassGetter: ModelClassGetter,
foreignKey?: string): Function;
export function HasMany(relatedClassGetter: ModelClassGetter,
export function HasMany(associatedClassGetter: ModelClassGetter,
options?: AssociationOptionsHasMany): Function;
export function HasMany(relatedClassGetter: ModelClassGetter,
export function HasMany(associatedClassGetter: ModelClassGetter,
optionsOrForeignKey?: string | AssociationOptionsHasMany): Function {

return (target: any, propertyName: string) => {
addAssociation(
target,
HAS_MANY,
relatedClassGetter,
propertyName,
optionsOrForeignKey,
const options: AssociationOptionsHasMany = getPreparedAssociationOptions(optionsOrForeignKey);
if (!options.as) options.as = propertyName;
addAssociation(target, new HasAssociation(
associatedClassGetter,
options,
Association.HasMany,
)
);
};
}
24 changes: 14 additions & 10 deletions lib/annotations/association/HasOne.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import {AssociationOptionsHasOne} from 'sequelize';
import {addAssociation, HAS_ONE} from "../../services/association";
import {Association} from "../../enums/Association";
import {ModelClassGetter} from "../../types/ModelClassGetter";
import {addAssociation, getPreparedAssociationOptions} from '../../services/association';
import {HasAssociation} from '../../models/association/HasAssociation';

export function HasOne(relatedClassGetter: ModelClassGetter,
export function HasOne(associatedClassGetter: ModelClassGetter,
foreignKey?: string): Function;
export function HasOne(relatedClassGetter: ModelClassGetter,
export function HasOne(associatedClassGetter: ModelClassGetter,
options?: AssociationOptionsHasOne): Function;
export function HasOne(relatedClassGetter: ModelClassGetter,
export function HasOne(associatedClassGetter: ModelClassGetter,
optionsOrForeignKey?: string | AssociationOptionsHasOne): Function {

return (target: any, propertyName: string) => {
addAssociation(
target,
HAS_ONE,
relatedClassGetter,
propertyName,
optionsOrForeignKey,
const options: AssociationOptionsHasOne = getPreparedAssociationOptions(optionsOrForeignKey);
if (!options.as) options.as = propertyName;
addAssociation(target, new HasAssociation(
associatedClassGetter,
options,
Association.HasOne,
)
);
};
}
6 changes: 6 additions & 0 deletions lib/enums/Association.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum Association {
BelongsToMany = 'belongsToMany',
BelongsTo = 'belongsTo',
HasMany = 'hasMany',
HasOne = 'hasOne',
}
12 changes: 12 additions & 0 deletions lib/interfaces/AssociationOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {
AssociationOptionsBelongsTo, AssociationOptionsHasMany,
AssociationOptionsHasOne, AssociationOptionsManyToMany
} from 'sequelize';
import {IPreparedAssociationOptionsBelongsToMany} from './IPreparedAssociationOptionsBelongsToMany';

export type AssociationOptions =
AssociationOptionsBelongsTo |
IPreparedAssociationOptionsBelongsToMany |
AssociationOptionsHasMany |
AssociationOptionsHasOne |
AssociationOptionsManyToMany;
3 changes: 2 additions & 1 deletion lib/interfaces/IAssociationOptionsBelongsToMany.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {AssociationForeignKeyOptions, AssociationOptionsManyToMany} from "sequelize";
import {ModelClassGetter} from "../types/ModelClassGetter";
import {IThroughOptions} from './IThroughOptions';

export interface IAssociationOptionsBelongsToMany extends AssociationOptionsManyToMany {
through: ModelClassGetter | string;
through: ModelClassGetter | string | IThroughOptions;
otherKey?: string | AssociationForeignKeyOptions;
}
8 changes: 5 additions & 3 deletions lib/interfaces/IFindOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {WhereOptions, LoggingOptions, SearchPathOptions, col, FindOptionsAttributesArray,
literal, fn} from 'sequelize';
import {
WhereOptions, LoggingOptions, SearchPathOptions, col, FindOptionsAttributesArray,
literal, fn, and, or
} from 'sequelize';
import {Model} from "../models/Model";
import {IIncludeOptions} from "./IIncludeOptions";

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

/**
* A list of the attributes that you want to select. To rename an attribute, you can pass an array, with
Expand Down
7 changes: 7 additions & 0 deletions lib/interfaces/IPreparedAssociationOptionsBelongsToMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {AssociationForeignKeyOptions, AssociationOptionsManyToMany} from "sequelize";
import {IPreparedThroughOptions} from './IPreparedThroughOptions';

export interface IPreparedAssociationOptionsBelongsToMany extends AssociationOptionsManyToMany {
through: IPreparedThroughOptions;
otherKey?: string | AssociationForeignKeyOptions;
}
30 changes: 30 additions & 0 deletions lib/interfaces/IPreparedThroughOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {AssociationScope} from 'sequelize';
import {Model} from '../models/Model';

/**
* Used for a association table in n:m associations.
*
* @see AssociationOptionsBelongsToMany
*/
export interface IPreparedThroughOptions {

/**
* The model used to join both sides of the N:M association.
*/
model: typeof Model;

/**
* A key/value set that will be used for association create and find defaults on the through model.
* (Remember to add the attributes to the through model)
*/
scope?: AssociationScope;

/**
* If true a unique key will be generated from the foreign keys used (might want to turn this off and create
* specific unique keys when using scopes)
*
* Defaults to true
*/
unique?: boolean;

}
14 changes: 0 additions & 14 deletions lib/interfaces/ISequelizeAssociation.ts

This file was deleted.

30 changes: 30 additions & 0 deletions lib/interfaces/IThroughOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {AssociationScope} from 'sequelize';
import {ModelClassGetter} from '../types/ModelClassGetter';

/**
* Used for a association table in n:m associations.
*
* @see AssociationOptionsBelongsToMany
*/
export interface IThroughOptions {

/**
* The model used to join both sides of the N:M association.
*/
model: ModelClassGetter | string;

/**
* A key/value set that will be used for association create and find defaults on the through model.
* (Remember to add the attributes to the through model)
*/
scope?: AssociationScope;

/**
* If true a unique key will be generated from the foreign keys used (might want to turn this off and create
* specific unique keys when using scopes)
*
* Defaults to true
*/
unique?: boolean;

}
15 changes: 11 additions & 4 deletions lib/models/BaseSequelize.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Model} from "./Model";
import {DEFAULT_DEFINE_OPTIONS, getModels} from "../services/models";
import {getAssociations, processAssociation} from "../services/association";
import {getAssociations} from "../services/association";
import {ISequelizeConfig} from "../interfaces/ISequelizeConfig";
import {ISequelizeUriConfig} from "../interfaces/ISequelizeUriConfig";
import {ISequelizeDbNameConfig} from "../interfaces/ISequelizeDbNameConfig";
Expand All @@ -9,7 +9,7 @@ import {resolveScopes} from "../services/scopes";
import {installHooks} from "../services/hooks";
import {ISequelizeValidationOnlyConfig} from "../interfaces/ISequelizeValidationOnlyConfig";
import {extend} from "../utils/object";
import {ISequelizeAssociation} from "../interfaces/ISequelizeAssociation";
import {BaseAssociation} from './association/BaseAssociation';

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

if (!associations) return;

associations.forEach(association => processAssociation(this, model, association));
associations.forEach(association => {
association.init(model, this);
const associatedClass = association.getAssociatedClass();
const relation = association.getAssociation();
const options = association.getSequelizeOptions();
model[relation](associatedClass, options);
this.adjustAssociation(model, association);
});
});
}

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

abstract adjustAssociation(model: any, association: ISequelizeAssociation): void;
abstract adjustAssociation(model: any, association: BaseAssociation): void;

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

Expand Down
Loading

0 comments on commit c569be6

Please sign in to comment.