Skip to content

Commit cece881

Browse files
refactor: modernize codebase
1 parent a77ccbe commit cece881

7 files changed

+102
-116
lines changed

src/decorators/inject-connection.decorator.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import { ConnectionManager } from 'typeorm';
22
import { Container, Constructable } from 'typedi';
3+
import { ConnectionNotFoundError } from '../errors/manager-not-found.error';
34

45
/**
5-
* Allows to inject an Connection using typedi's Container.
6+
* Injects the `Connection` object using TypeDI's container.
7+
* This decorator can be used both as class property decorator or constructor parameter decorator.
68
*/
7-
export function InjectConnection(connectionName: string = 'default'): Function {
8-
return function (object: Object | Function, propertyName: string, index?: number) {
9+
export function InjectConnection(connectionName: string = 'default'): CallableFunction {
10+
return function (object: Object, propertyName: string | symbol, index?: number): void {
911
Container.registerHandler({
1012
object: object as Constructable<unknown>,
11-
index,
12-
propertyName,
13+
index: index,
14+
propertyName: propertyName as string,
1315
value: containerInstance => {
1416
const connectionManager = containerInstance.get(ConnectionManager);
15-
if (!connectionManager.has(connectionName))
16-
throw new Error(
17-
`Cannot get connection "${connectionName}" from the connection manager. ` +
18-
`Make sure you have created such connection. Also make sure you have called useContainer(Container) ` +
19-
`in your application before you established a connection and importing any entity.`
20-
);
17+
18+
if (!connectionManager.has(connectionName)) {
19+
throw new ConnectionNotFoundError(connectionName);
20+
}
2121

2222
return connectionManager.get(connectionName);
2323
},

src/decorators/inject-manager.decorator.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,25 @@
11
import { ConnectionManager } from 'typeorm';
22
import { Constructable, Container } from 'typedi';
3+
import { ConnectionNotFoundError } from '../errors/manager-not-found.error';
34

45
/**
5-
* Allows to inject an EntityManager using typedi's Container.
6+
* Injects the `EntityManager` object using TypeDI's container.
7+
* This decorator can be used both as class property decorator or constructor parameter decorator.
68
*/
7-
export function InjectManager(connectionName: string = 'default'): Function {
8-
return function (object: Object | Function, propertyName: string, index?: number) {
9+
export function InjectManager(connectionName: string = 'default'): CallableFunction {
10+
return function (object: Object, propertyName: string | symbol, index?: number): void {
911
Container.registerHandler({
1012
object: object as Constructable<unknown>,
11-
index,
12-
propertyName,
13+
index: index,
14+
propertyName: propertyName as string,
1315
value: containerInstance => {
1416
const connectionManager = containerInstance.get(ConnectionManager);
15-
if (!connectionManager.has(connectionName))
16-
throw new Error(
17-
`Cannot get connection "${connectionName}" from the connection manager. ` +
18-
`Make sure you have created such connection. Also make sure you have called useContainer(Container) ` +
19-
`in your application before you established a connection and importing any entity.`
20-
);
2117

22-
const connection = connectionManager.get(connectionName);
23-
const entityManager = connection.manager;
24-
if (!entityManager)
25-
throw new Error(
26-
`Entity manager was not found on "${connectionName}" connection. ` +
27-
`Make sure you correctly setup connection and container usage.`
28-
);
18+
if (!connectionManager.has(connectionName)) {
19+
throw new ConnectionNotFoundError(connectionName);
20+
}
2921

30-
return entityManager;
22+
return connectionManager.get(connectionName).manager;
3123
},
3224
});
3325
};
Lines changed: 51 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
1-
import { ConnectionManager, Repository, TreeRepository, MongoRepository } from 'typeorm';
1+
import { ConnectionManager, Repository, TreeRepository, MongoRepository, EntityTarget, ObjectType } from 'typeorm';
22
import { Constructable, Container, ContainerInstance } from 'typedi';
33

44
import { EntityTypeMissingError } from '../errors/entity-type-missing.error';
55
import { PropertyTypeMissingError } from '../errors/property-type-missing.error';
66
import { ParamTypeMissingError } from '../errors/param-type-missing.error';
7+
import { ConnectionNotFoundError } from '../errors/manager-not-found.error';
78

89
/**
910
* Helper to avoid V8 compilation of anonymous function on each call of decorator.
1011
*/
11-
function getRepository(
12+
function getRepositoryHelper(
1213
connectionName: string,
13-
repositoryType: Function,
14-
entityType: Function,
14+
repositoryType: ObjectType<unknown>,
15+
entityType: EntityTarget<unknown>,
1516
containerInstance: ContainerInstance
1617
) {
1718
const connectionManager = containerInstance.get(ConnectionManager);
1819
if (!connectionManager.has(connectionName)) {
19-
throw new Error(
20-
`Cannot get connection "${connectionName}" from the connection manager. ` +
21-
`Make sure you have created such connection. Also make sure you have called useContainer(Container) ` +
22-
`in your application before you established a connection and importing any entity.`
23-
);
20+
throw new ConnectionNotFoundError(connectionName);
2421
}
22+
2523
const connection = connectionManager.get(connectionName);
2624

2725
switch (repositoryType) {
@@ -31,97 +29,63 @@ function getRepository(
3129
return connection.getMongoRepository(entityType);
3230
case TreeRepository:
3331
return connection.getTreeRepository(entityType);
34-
// if not the TypeORM's ones, there must be custom repository classes
3532
default:
33+
/** If the requested type is not well-known, then it must be a custom repository. */
3634
return connection.getCustomRepository(repositoryType);
3735
}
3836
}
3937

4038
/**
41-
* Satisfy typescript compiler about universal decorators.
42-
*/
43-
export type ParamOrPropDecorator = (object: object, propertyName: string, index?: number) => void;
44-
45-
/**
46-
* Allows to inject a custom repository using TypeDI's Container.
47-
* Be aware that you have to annotate the param/property with correct type!
48-
* ```ts
49-
* class Sample {
50-
* // constructor injection
51-
* constructor(
52-
* \@InjectRepository()
53-
* private userRepository: UserRepository,
54-
* ) {}
39+
* Injects the requested custom repository object using TypeDI's container. To make injection work without explicity
40+
* specifying the type in the decorator, you must annotate your properties and/or parameters with the correct type!
5541
*
56-
* // property injection
42+
* ```ts
43+
* class SampleClass {
5744
* \@InjectRepository()
5845
* userRepository: UserRepository;
46+
*
47+
* constructor(@InjectRepository() private userRepository: UserRepository) {}
5948
* }
6049
* ```
6150
*/
62-
export function InjectRepository(): ParamOrPropDecorator;
51+
export function InjectRepository(): CallableFunction;
52+
export function InjectRepository(connectionName: string): CallableFunction;
53+
6354
/**
64-
* Allows to inject a Repository, MongoRepository, TreeRepository using TypeDI's Container.
65-
* Be aware that you have to annotate the param/property with correct type!
66-
* ```ts
67-
* class Sample {
68-
* // constructor injection
69-
* constructor(
70-
* \@InjectRepository(User)
71-
* private userRepository: Repository<User>,
72-
* ) {}
55+
* Injects the requested `Repository`, `MongoRepository`, `TreeRepository` object using TypeDI's container.
56+
* To make injection work without explicity specifying the type in the decorator, you must annotate your properties
57+
* and/or parameters with the correct type!
7358
*
74-
* // property injection
59+
* ```ts
60+
* class SampleClass {
7561
* \@InjectRepository(User)
7662
* userRepository: Repository<User>;
77-
* }
78-
* ```
79-
*/
80-
export function InjectRepository(entityType: Function): ParamOrPropDecorator;
81-
/**
82-
* Allows to inject a custom repository using TypeDI's Container
83-
* and specify the connection name in a parameter.
84-
* Be aware that you have to annotate the param/property with correct type!
85-
* ```ts
86-
* class Sample {
87-
* // constructor injection
88-
* constructor(
89-
* \@InjectRepository("test-conn")
90-
* private userRepository: UserRepository,
91-
* ) {}
9263
*
93-
* // property injection
94-
* \@InjectRepository("test-conn")
95-
* userRepository: UserRepository;
64+
* constructor(@InjectRepository(User) private userRepository: Repository<User>) {}
9665
* }
9766
* ```
9867
*/
99-
export function InjectRepository(connectionName: string): ParamOrPropDecorator;
68+
export function InjectRepository(entityType: Function): CallableFunction;
10069
/**
101-
* Allows to inject a Repository, MongoRepository, TreeRepository using TypeDI's Container
102-
* and specify the connection name in a parameter.
103-
* Be aware that you have to annotate the param/property with correct type!
104-
* ```ts
105-
* class Sample {
106-
* // constructor injection
107-
* constructor(
108-
* \@InjectRepository(User, "test-conn")
109-
* private userRepository: Repository<User>,
110-
* ) {}
70+
* Injects the requested `Repository`, `MongoRepository`, `TreeRepository` object using TypeDI's container.
71+
* To make injection work without explicity specifying the type in the decorator, you must annotate your properties
72+
* and/or parameters with the correct type!
11173
*
112-
* // property injection
74+
* ```ts
75+
* class SampleClass {
11376
* \@InjectRepository(User, "test-conn")
11477
* userRepository: Repository<User>;
78+
*
79+
* constructor(@InjectRepository(User, "test-conn") private userRepository: Repository<User>) {}
11580
* }
11681
* ```
11782
*/
118-
export function InjectRepository(entityType: Function, connectionName: string): ParamOrPropDecorator;
119-
83+
export function InjectRepository(entityType: Function, connectionName: string): CallableFunction;
12084
export function InjectRepository(
12185
entityTypeOrConnectionName?: Function | string,
12286
paramConnectionName = 'default'
123-
): ParamOrPropDecorator {
124-
return (object: object, propertyName: string, index?: number) => {
87+
): CallableFunction {
88+
return (object: object, propertyName: string | symbol, index?: number): void => {
12589
let entityType: Function | undefined;
12690
let connectionName: string;
12791
let repositoryType: Function;
@@ -134,20 +98,27 @@ export function InjectRepository(
13498
entityType = entityTypeOrConnectionName;
13599
}
136100

137-
// if the decorator has been aplied to parameter (constructor injection)
101+
if (Reflect?.getOwnMetadata == undefined) {
102+
throw new Error('Reflect.getOwnMetadata is not defined. Make sure to import the `reflect-metadata` package!');
103+
}
104+
138105
if (index !== undefined) {
106+
/** The decorator has been applied to a constructor parameter. */
139107
const paramTypes: Function[] | undefined = Reflect.getOwnMetadata('design:paramtypes', object, propertyName);
108+
140109
if (!paramTypes || !paramTypes[index]) {
141-
throw new ParamTypeMissingError(object, propertyName, index);
110+
throw new ParamTypeMissingError(object, propertyName as string, index);
142111
}
112+
143113
repositoryType = paramTypes[index];
144-
}
145-
// if the parameter has been aplied to class property
146-
else {
114+
} else {
115+
/** The decorator has been applied to a class property. */
147116
const propertyType: Function | undefined = Reflect.getOwnMetadata('design:type', object, propertyName);
117+
148118
if (!propertyType) {
149-
throw new PropertyTypeMissingError(object, propertyName);
119+
throw new PropertyTypeMissingError(object, propertyName as string);
150120
}
121+
151122
repositoryType = propertyType;
152123
}
153124

@@ -156,15 +127,15 @@ export function InjectRepository(
156127
case MongoRepository:
157128
case TreeRepository:
158129
if (!entityType) {
159-
throw new EntityTypeMissingError(object, propertyName, index);
130+
throw new EntityTypeMissingError(object, propertyName as string, index);
160131
}
161132
}
162133

163134
Container.registerHandler({
164-
index,
165135
object: object as Constructable<unknown>,
166-
propertyName,
167-
value: containerInstance => getRepository(connectionName, repositoryType, entityType!, containerInstance),
136+
index: index,
137+
propertyName: propertyName as string,
138+
value: containerInstance => getRepositoryHelper(connectionName, repositoryType, entityType!, containerInstance),
168139
});
169140
};
170141
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export class EntityTypeMissingError extends Error {
2+
public name = 'EntityTypeMissingError';
3+
24
constructor(object: Object, propertyName: string, index?: number) {
35
super(
46
`Missing "entityType" parameter of "@InjectRepository" decorator ` + index !== undefined
@@ -8,7 +10,5 @@ export class EntityTypeMissingError extends Error {
810
`you have to specify the entity type due to TS reflection limitation - ` +
911
`"entityType" parameter can be ommited only for custom repositories.`
1012
);
11-
12-
Object.setPrototypeOf(this, EntityTypeMissingError.prototype);
1313
}
1414
}

src/errors/manager-not-found.error.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Raised when the requested connection doesn't exists in TypeORM.
3+
*/
4+
export class ConnectionNotFoundError extends Error {
5+
public name = 'ManagerNotFoundError';
6+
7+
public get message() {
8+
return (
9+
`Cannot get Connection with name "${this.connectionName}" from the ConnectionManager. ` +
10+
`Make sure you have created the connection and called "useContainer(Container)" in your application ` +
11+
`before establishing a connection and importing any entity into TypeORM.`
12+
);
13+
}
14+
15+
/**
16+
* Creates a new ManagerNotFoundError what is raised when we cannot request a manager from TypeORM.
17+
* @param type type of the requested manager
18+
* @param connectionName optional name of the manager
19+
*/
20+
constructor(private connectionName?: string) {
21+
super();
22+
}
23+
}

src/errors/param-type-missing.error.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export class ParamTypeMissingError extends Error {
2+
public name = 'ParamTypeMissingError';
3+
24
constructor(object: Object, propertyName: string, index: number) {
35
super(
46
`Cannot get reflected type for a "${propertyName}" method's ${index + 1}. parameter of ${
@@ -9,7 +11,5 @@ export class ParamTypeMissingError extends Error {
911
`And make sure that you have annotated the property type correctly with: ` +
1012
`Repository, MongoRepository, TreeRepository or custom repository class type.`
1113
);
12-
13-
Object.setPrototypeOf(this, ParamTypeMissingError.prototype);
1414
}
1515
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export class PropertyTypeMissingError extends Error {
2+
public name = 'PropertyTypeMissingError';
3+
24
constructor(object: Object, propertyName: string) {
35
super(
46
`Cannot get reflected type for a property "${propertyName}" of ${object.constructor.name} class. ` +
@@ -7,7 +9,5 @@ export class PropertyTypeMissingError extends Error {
79
`And make sure that you have annotated the property type correctly with: ` +
810
`Repository, MongoRepository, TreeRepository or custom repository class type.`
911
);
10-
11-
Object.setPrototypeOf(this, PropertyTypeMissingError.prototype);
1212
}
1313
}

0 commit comments

Comments
 (0)