Skip to content

Commit b886442

Browse files
[#39] Refactoring: improve error handling
- created DecoratorUtils.getSubjectName() and used it in DecoratorUsageError-s - throwing DecoratorUsageError when decorators used on wrong [class/method/property] , solving issue [#22] - changed @autowire() to @Autowired()
1 parent 5b4edcc commit b886442

29 files changed

+300
-77
lines changed

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export {ComponentScan} from "./lib/decorators/ComponentScanDecorator";
77
export {Import} from "./lib/decorators/ImportDecorator";
88
export {Configuration} from "./lib/decorators/ConfigurationDecorator";
99
export {Controller} from "./lib/decorators/ControllerDecorator";
10-
export {Inject, Value, Autowire} from "./lib/decorators/InjectionDecorators";
10+
export {Inject, Value, Autowired} from "./lib/decorators/InjectionDecorators";
1111
export {PostConstruct, PreDestroy} from "./lib/decorators/LifeCycleHooksDecorators"
1212
export {Profile} from "./lib/decorators/ComponentDecorator";
1313
export {PropertySource} from "./lib/decorators/PropertySourceDecorator";

src/lib/decorators/ComponentDecorator.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { InjectUtil, InjectionData } from "./InjectionDecorators";
22
import { CONTROLLER_DECORATOR_TOKEN } from "./ControllerDecorator";
33
import { INTERCEPTOR_DECORATOR_TOKEN } from "../interceptors/InterceptorDecorator";
44
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
5+
import { DecoratorUtil, DecoratorType } from "../helpers/DecoratorUtils";
56

67
export class ComponentData {
78
classToken: Symbol;
@@ -20,6 +21,11 @@ const COMPONENT_DECORATOR_TOKEN = Symbol('component_decorator_token');
2021

2122
export function Component() {
2223
return function (target) {
24+
let args = Array.prototype.slice.call(arguments);
25+
if (!DecoratorUtil.isType(DecoratorType.CLASS, args)) {
26+
let subjectName = DecoratorUtil.getSubjectName(args);
27+
throw new DecoratorUsageError(`@Component can be set only on a class! (${subjectName})`);
28+
}
2329
let componentData = new ComponentData();
2430
componentData.injectionData = InjectUtil.initIfDoesntExist(target.prototype);
2531
target[COMPONENT_DECORATOR_TOKEN] = componentData;
@@ -29,7 +35,8 @@ export function Component() {
2935
export function Profile(profile: string) {
3036
return function (target) {
3137
if (!ComponentUtil.isComponent(target)) {
32-
throw new DecoratorUsageError(`@Profile can be set only on @Component! (${target.name})`);
38+
let subjectName = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
39+
throw new DecoratorUsageError(`@Profile can be set only on @Component! (${subjectName})`);
3340
}
3441
ComponentUtil.getComponentData(target).profile = profile;
3542
};

src/lib/decorators/ComponentScanDecorator.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ConfigurationData, ConfigurationUtil } from "./ConfigurationDecorator";
55
import { ComponentUtil } from "./ComponentDecorator";
66
import { RequireUtils } from "../helpers/RequireUtils";
77
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
8+
import { DecoratorUtil } from "../helpers/DecoratorUtils";
89

910
/**
1011
*A decorator for setting up project files to be component-scanned.
@@ -14,7 +15,8 @@ import { DecoratorUsageError } from "../errors/DecoratorUsageError";
1415
export function ComponentScan(path) {
1516
return function (target) {
1617
if (!ConfigurationUtil.isConfigurationClass(target)) {
17-
throw new DecoratorUsageError(`@ComponentScan is allowed on @Configuration classes only! (${target.name})`);
18+
let subjectName = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
19+
throw new DecoratorUsageError(`@ComponentScan is allowed on @Configuration classes only! (${subjectName})`);
1820
}
1921
ConfigurationUtil.addComponentScanPath(target, path);
2022
};

src/lib/decorators/ConfigurationDecorator.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ComponentFactory } from "../di/ComponentFactory";
22
import { PropertySourceUtil } from "./PropertySourceDecorator";
33
import { ComponentScanUtil } from "./ComponentScanDecorator";
44
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
5+
import { DecoratorUtil, DecoratorType } from "../helpers/DecoratorUtils";
56

67
const CONFIGURATION_HOLDER_TOKEN = Symbol('configuration_holder_token');
78

@@ -38,6 +39,10 @@ export class ConfigurationData {
3839

3940
export function Configuration() {
4041
return function (target) {
42+
if (!DecoratorUtil.isType(DecoratorType.CLASS, Array.prototype.slice.call(arguments))) {
43+
let subjectName = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
44+
throw new DecoratorUsageError(`@Configuration can be set only on classes! (${subjectName})`);
45+
}
4146
if (target[CONFIGURATION_HOLDER_TOKEN]) {
4247
throw new DecoratorUsageError(`Duplicate @Configuration decorator' (${target.name})`);
4348
}
@@ -51,7 +56,8 @@ export class ConfigurationUtil {
5156

5257
static getConfigurationData(target): ConfigurationData {
5358
if (!this.isConfigurationClass(target)) {
54-
throw new Error(`${target.name} is not a @Configuration class`);
59+
let subjectName = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
60+
throw new Error(`${subjectName} is not a @Configuration class`);
5561
}
5662
return target[CONFIGURATION_HOLDER_TOKEN];
5763
}

src/lib/decorators/ControllerDecorator.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { Component } from "./ComponentDecorator";
2+
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
3+
import { DecoratorUtil, DecoratorType } from "../helpers/DecoratorUtils";
24
export const CONTROLLER_DECORATOR_TOKEN = Symbol('controller_decorator_token');
35

46
export function Controller() {
57
return function (target) {
8+
if (!DecoratorUtil.isType(DecoratorType.CLASS, Array.prototype.slice.call(arguments))) {
9+
let subjectName = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
10+
throw new DecoratorUsageError(`@Configuration can be set only on classes! (${subjectName})`);
11+
}
612
Component()(target);
713
target[CONTROLLER_DECORATOR_TOKEN] = true;
814
};

src/lib/decorators/ImportDecorator.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ConfigurationUtil } from "./ConfigurationDecorator";
22
import { BadArgumentError } from "../errors/BadArgumentError";
33
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
4+
import { DecoratorUtil } from "../helpers/DecoratorUtils";
45

56
/**
67
* Decorator used for composing configuration classes by importing other configuration classes.
@@ -11,8 +12,8 @@ import { DecoratorUsageError } from "../errors/DecoratorUsageError";
1112
export function Import(...configurationClasses) {
1213
return function (targetConfigurationClass) {
1314
if (!ConfigurationUtil.isConfigurationClass(targetConfigurationClass)) {
14-
// tslint:disable-next-line
15-
throw new DecoratorUsageError(`@Import is allowed on @Configuration classes only! (${targetConfigurationClass.name})`);
15+
let subjectName = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
16+
throw new DecoratorUsageError(`@Import is allowed on @Configuration classes only! (${subjectName})`);
1617
}
1718
let targetConfigurationData = ConfigurationUtil.getConfigurationData(targetConfigurationClass);
1819
for (let configurationClass of configurationClasses) {

src/lib/decorators/InjectionDecorators.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { ComponentUtil } from "./ComponentDecorator";
22
import { TypeUtils } from "../helpers/TypeUtils";
33
import { InjectionError } from "../errors/InjectionError";
4+
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
5+
import { DecoratorType, DecoratorUtil } from "../helpers/DecoratorUtils";
46

57
const INJECT_DECORATOR_TOKEN = Symbol('injector_decorator_token');
68

@@ -24,15 +26,22 @@ export class InjectionData {
2426
}
2527

2628
export function Inject(dependencyToken?: Symbol) {
27-
return function (target: any, fieldName: string) {
29+
return function (...args) {
30+
if (!DecoratorUtil.isType(DecoratorType.PROPERTY, args)) {
31+
let subject = DecoratorUtil.getSubjectName(args);
32+
throw new DecoratorUsageError(`@Inject can be set only on properties of a @Component class! (${subject})`);
33+
}
34+
let target = args[0];
35+
let fieldName = args[1];
2836
let token = dependencyToken;
2937
let type = (<any> Reflect).getMetadata('design:type', target, fieldName);
3038
if (!token) {
3139
// fallback to field type
3240
if (ComponentUtil.isComponent(type)) {
3341
token = ComponentUtil.getClassToken(type);
3442
} else {
35-
throw new InjectionError(`Cannot inject dependency which is not a @Component! (${type.name})`);
43+
let sub = DecoratorUtil.getSubjectName(args);
44+
throw new InjectionError(`Cannot inject dependency (${type.name}) which is not a @Component! (${sub})`);
3645
}
3746
}
3847
// NOTE assumption: if type not declared or any then type is Object and isArray is false
@@ -41,12 +50,23 @@ export function Inject(dependencyToken?: Symbol) {
4150
};
4251
}
4352

44-
export function Autowire() {
45-
return Inject();
53+
export function Autowired() {
54+
return function (...args) {
55+
if (!DecoratorUtil.isType(DecoratorType.PROPERTY, args)) {
56+
let subj = DecoratorUtil.getSubjectName(args);
57+
throw new DecoratorUsageError(`@Autowired can be set only on properties of a @Component class! (${subj})`);
58+
}
59+
return Inject()(...args);
60+
};
4661
}
4762

4863
export function Value(preopertyKey) {
4964
return function (target: any, fieldName: string) {
65+
let args = Array.prototype.slice.call(arguments);
66+
if (!DecoratorUtil.isType(DecoratorType.PROPERTY, args)) {
67+
let subject = DecoratorUtil.getSubjectName(args);
68+
throw new DecoratorUsageError(`@Value can be set only on properties of a @Component class! (${subject})`);
69+
}
5070
InjectUtil.initIfDoesntExist(target).properties.set(fieldName, preopertyKey);
5171
};
5272
}

src/lib/decorators/LifeCycleHooksDecorators.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
2+
import { DecoratorUtil, DecoratorType } from "../helpers/DecoratorUtils";
23

34
const LIFE_CYCLE_HOOKS_TOKEN = Symbol('life_cycle_hooks_token');
45

@@ -12,11 +13,17 @@ export class LifeCycleHooksConfig {
1213
*/
1314
export function PostConstruct() {
1415
return function (target, methodName, descriptor: PropertyDescriptor) {
16+
let args = Array.prototype.slice.call(arguments);
17+
if (!DecoratorUtil.isType(DecoratorType.METHOD, args)) {
18+
let sub = DecoratorUtil.getSubjectName(args);
19+
throw new DecoratorUsageError(`@PostConstruct can be set only on methods of a @Component class! (${sub})`);
20+
}
1521
let conf = LifeCycleHooksUtil.initIfDoesntExist(target);
1622
if (conf.postConstructMethod) {
1723
let errorParams = [conf.postConstructMethod, methodName].join(', ');
24+
let subjectName = DecoratorUtil.getSubjectName(args);
1825
// tslint:disable-next-line
19-
throw new DecoratorUsageError(`@PostConstruct used on multiple methods (${errorParams}) within a component`);
26+
throw new DecoratorUsageError(`@PostConstruct used on multiple methods (${errorParams}) within a @Component (${subjectName})`);
2027
}
2128
conf.postConstructMethod = methodName;
2229
};
@@ -27,10 +34,17 @@ export function PostConstruct() {
2734
*/
2835
export function PreDestroy() {
2936
return function (target, methodName, descriptor: PropertyDescriptor) {
37+
let args = Array.prototype.slice.call(arguments);
38+
if (!DecoratorUtil.isType(DecoratorType.METHOD, args)) {
39+
let subject = DecoratorUtil.getSubjectName(args);
40+
throw new DecoratorUsageError(`@PreDestroy can be set only on methods of a @Component class! (${subject})`);
41+
}
3042
let conf = LifeCycleHooksUtil.initIfDoesntExist(target);
3143
if (conf.preDestroyMethod) {
3244
let errorParams = [conf.preDestroyMethod, methodName].join(', ');
33-
throw new DecoratorUsageError(`@PreDestroy used on multiple methods within a component (${errorParams})`);
45+
let subjectName = DecoratorUtil.getSubjectName(args);
46+
// tslint:disable-next-line
47+
throw new DecoratorUsageError(`@PreDestroy used on multiple methods (${errorParams}) within a @Component (${subjectName})`);
3448
}
3549
conf.preDestroyMethod = methodName;
3650
};

src/lib/decorators/PropertySourceDecorator.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ConfigurationUtil } from "./ConfigurationDecorator";
33
import { GeneralUtils } from "../helpers/GeneralUtils";
44
import {RequireUtils} from "../helpers/RequireUtils";
55
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
6+
import { DecoratorUtil } from "../helpers/DecoratorUtils";
67

78
/**
89
* A decorator for defining a JSON property source for the configuration properties.
@@ -12,8 +13,8 @@ import { DecoratorUsageError } from "../errors/DecoratorUsageError";
1213
export function PropertySource(path: string) {
1314
return function (target) {
1415
if (!ConfigurationUtil.isConfigurationClass(target)) {
15-
// tslint:disable-next-line
16-
throw new DecoratorUsageError(`@PropertySource can be used only on @Configuration classes! (${target.name})`);
16+
let subject = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
17+
throw new DecoratorUsageError(`@PropertySource is allowed on @Configuration classes only! (${subject})`);
1718
}
1819
ConfigurationUtil.addPropertySourcePath(target, path);
1920
};

src/lib/decorators/QualifierDecorator.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { ComponentUtil } from "./ComponentDecorator";
22
import { DecoratorUsageError } from "../errors/DecoratorUsageError";
3+
import { DecoratorUtil } from "../helpers/DecoratorUtils";
34

45
export function Qualifier(token: Symbol) {
56
return function (target) {
67
if (!ComponentUtil.isComponent(target)) {
7-
throw new DecoratorUsageError(`@Qualifier can be used only on @Component classes! (${target.name})`);
8+
let subjectName = DecoratorUtil.getSubjectName(Array.prototype.slice.call(arguments));
9+
throw new DecoratorUsageError(`@Qualifier can be used only on @Component classes! (${subjectName})`);
810
}
911
ComponentUtil.getAliasTokens(target).push(token);
1012
};

0 commit comments

Comments
 (0)