@athenna/http 4.5.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@athenna/http",
3
- "version": "4.5.0",
3
+ "version": "4.7.0",
4
4
  "description": "The Athenna Http server. Built on top of fastify.",
5
5
  "license": "MIT",
6
6
  "author": "João Lenon <lenon@athenna.io>",
@@ -67,16 +67,16 @@
67
67
  "#tests": "./tests/index.js"
68
68
  },
69
69
  "dependencies": {
70
- "fastify": "^4.21.0"
70
+ "fastify": "^4.22.0"
71
71
  },
72
72
  "devDependencies": {
73
- "@athenna/artisan": "^4.2.0",
74
- "@athenna/common": "^4.4.0",
75
- "@athenna/config": "^4.3.0",
76
- "@athenna/ioc": "^4.1.0",
77
- "@athenna/logger": "^4.2.0",
78
- "@athenna/test": "^4.4.0",
79
- "@athenna/view": "^4.1.0",
73
+ "@athenna/artisan": "^4.10.0",
74
+ "@athenna/common": "^4.10.1",
75
+ "@athenna/config": "^4.4.0",
76
+ "@athenna/ioc": "^4.4.0",
77
+ "@athenna/logger": "^4.4.0",
78
+ "@athenna/test": "^4.7.0",
79
+ "@athenna/view": "^4.3.0",
80
80
  "@fastify/cors": "^8.1.1",
81
81
  "@fastify/helmet": "^10.0.2",
82
82
  "@fastify/rate-limit": "^7.5.0",
@@ -86,7 +86,6 @@
86
86
  "@typescript-eslint/parser": "^5.56.0",
87
87
  "c8": "^7.12.0",
88
88
  "commitizen": "^4.2.6",
89
- "cross-env": "^7.0.3",
90
89
  "cz-conventional-changelog": "^3.3.0",
91
90
  "eslint": "^8.36.0",
92
91
  "eslint-config-prettier": "^8.8.0",
@@ -142,7 +141,7 @@
142
141
  },
143
142
  "prettier": {
144
143
  "singleQuote": true,
145
- "trailingComma": "all",
144
+ "trailingComma": "none",
146
145
  "arrowParens": "avoid",
147
146
  "endOfLine": "lf",
148
147
  "semi": false,
@@ -209,37 +208,34 @@
209
208
  "services": [],
210
209
  "providers": [],
211
210
  "controllers": [
212
- "#tests/stubs/controllers/HelloController",
213
- "#tests/stubs/controllers/DecoratedController"
211
+ "#tests/fixtures/controllers/HelloController",
212
+ "#tests/fixtures/controllers/AnnotatedController"
214
213
  ],
215
214
  "middlewares": [
216
- "#tests/stubs/middlewares/Middleware",
217
- "#tests/stubs/middlewares/ImportedMiddleware",
218
- "#tests/stubs/middlewares/DecoratedMiddleware",
219
- "#tests/stubs/middlewares/DecoratedInterceptor",
220
- "#tests/stubs/middlewares/DecoratedTerminator"
215
+ "#tests/fixtures/middlewares/MyMiddleware",
216
+ "#tests/fixtures/middlewares/ImportedMiddleware"
221
217
  ],
222
218
  "namedMiddlewares": {
223
- "middleware": "#tests/stubs/middlewares/Middleware",
224
- "interceptor": "#tests/stubs/middlewares/Interceptor",
225
- "terminator": "#tests/stubs/middlewares/Terminator",
226
- "not-found-middleware": "#tests/stubs/middlewares/DecoratedMiddleware",
227
- "not-found-interceptor": "#tests/stubs/middlewares/DecoratedInterceptor",
228
- "not-found-terminator": "#tests/stubs/middlewares/DecoratedTerminator"
219
+ "myMiddleware": "#tests/fixtures/middlewares/MyMiddleware",
220
+ "myInterceptor": "#tests/fixtures/middlewares/MyInterceptor",
221
+ "myTerminator": "#tests/fixtures/middlewares/MyTerminator",
222
+ "not-found-middleware": "#tests/fixtures/middlewares/AnnotatedMiddleware",
223
+ "not-found-interceptor": "#tests/fixtures/middlewares/AnnotatedInterceptor",
224
+ "not-found-terminator": "#tests/fixtures/middlewares/AnnotatedTerminator"
229
225
  },
230
226
  "globalMiddlewares": [
231
- "#tests/stubs/middlewares/Middleware",
232
- "#tests/stubs/middlewares/Interceptor",
233
- "#tests/stubs/middlewares/Terminator",
234
- "#tests/stubs/middlewares/DecoratedGlobalMiddleware",
235
- "#tests/stubs/middlewares/DecoratedGlobalInterceptor",
236
- "#tests/stubs/middlewares/DecoratedGlobalTerminator"
227
+ "#tests/fixtures/middlewares/MyMiddleware",
228
+ "#tests/fixtures/middlewares/MyInterceptor",
229
+ "#tests/fixtures/middlewares/MyTerminator",
230
+ "#tests/fixtures/middlewares/AnnotatedGlobalMiddleware",
231
+ "#tests/fixtures/middlewares/AnnotatedGlobalInterceptor",
232
+ "#tests/fixtures/middlewares/AnnotatedGlobalTerminator"
237
233
  ],
238
234
  "commands": {
239
235
  "route:list": {
240
236
  "path": "#src/commands/RouteListCommand",
241
- "route": "./tests/stubs/routes/http.js",
242
- "kernel": "./tests/stubs/kernels/HttpKernel.js"
237
+ "route": "./tests/fixtures/routes/http.js",
238
+ "kernel": "./tests/fixtures/kernels/HttpKernel.js"
243
239
  },
244
240
  "make:controller": "#src/commands/MakeControllerCommand",
245
241
  "make:interceptor": "#src/commands/MakeInterceptorCommand",
@@ -9,6 +9,7 @@
9
9
  import 'reflect-metadata';
10
10
  import { debug } from '#src/debug';
11
11
  import { Options } from '@athenna/common';
12
+ import { Annotation } from '@athenna/ioc';
12
13
  /**
13
14
  * Create a controller inside the service provider.
14
15
  */
@@ -16,15 +17,16 @@ export function Controller(options) {
16
17
  return (target) => {
17
18
  options = Options.create(options, {
18
19
  alias: `App/Http/Controllers/${target.name}`,
19
- type: 'transient',
20
+ type: 'transient'
20
21
  });
21
- const alias = options.alias;
22
- const createCamelAlias = false;
23
- if (ioc.hasDependency(alias)) {
24
- debug('Controller %s was already registered in the service container. Skipping registration via Controller annotation.', alias);
22
+ debug('Registering controller metadata for the service container %o', {
23
+ name: target.name,
24
+ ...options
25
+ });
26
+ if (ioc.has(options.alias) || ioc.has(options.camelAlias)) {
27
+ debug('Skipping registration, controller is already registered.');
25
28
  return;
26
29
  }
27
- ioc[options.type](alias, target, createCamelAlias);
28
- Reflect.defineMetadata('ioc:registered', true, target);
30
+ Annotation.defineMeta(target, options);
29
31
  };
30
32
  }
@@ -7,9 +7,9 @@
7
7
  * file that was distributed with this source code.
8
8
  */
9
9
  import 'reflect-metadata';
10
- import { Server } from '#src/facades/Server';
10
+ import { debug } from '#src/debug';
11
+ import { Annotation } from '@athenna/ioc';
11
12
  import { Options, String } from '@athenna/common';
12
- import { debug } from '#src/debug/index';
13
13
  /**
14
14
  * Create an interceptor inside the service provider.
15
15
  */
@@ -17,24 +17,24 @@ export function Interceptor(options) {
17
17
  return (target) => {
18
18
  options = Options.create(options, {
19
19
  isGlobal: false,
20
- name: String.toCamelCase(target.name),
21
- alias: `App/Http/Interceptors/${target.name}`,
22
20
  type: 'transient',
21
+ alias: `App/Http/Interceptors/${target.name}`,
22
+ name: String.toCamelCase(target.name)
23
+ });
24
+ options.name = `App/Http/Interceptors/Names/${options.name}`;
25
+ debug('Registering interceptor metadata for the service container %o', {
26
+ ...options,
27
+ name: target.name,
28
+ namedAlias: options.name
23
29
  });
24
- const alias = options.alias;
25
- const createCamelAlias = false;
26
- if (ioc.hasDependency(alias)) {
27
- debug('Interceptor %s was already registered in the service container. Skipping registration via Interceptor annotation.', alias);
30
+ if (ioc.has(options.name)) {
31
+ debug('Skipping registration, named alias %s is already registered.', options.name);
28
32
  return;
29
33
  }
30
- ioc[options.type](alias, target, createCamelAlias);
31
- Reflect.defineMetadata('ioc:registered', true, target);
32
- if (!options.isGlobal) {
33
- ioc.alias(`App/Http/Interceptors/Names/${options.name}`, alias);
34
+ if (ioc.has(options.alias)) {
35
+ debug('Skipping registration, alias %s is already registered.', options.alias);
34
36
  return;
35
37
  }
36
- const Interceptor = ioc.safeUse(alias);
37
- debug('Registering %s as a global interceptor via Interceptor annotation.', Interceptor.constructor.name);
38
- Server.intercept(Interceptor.intercept);
38
+ Annotation.defineMeta(target, options);
39
39
  };
40
40
  }
@@ -7,9 +7,9 @@
7
7
  * file that was distributed with this source code.
8
8
  */
9
9
  import 'reflect-metadata';
10
- import { Server } from '#src/facades/Server';
10
+ import { debug } from '#src/debug';
11
+ import { Annotation } from '@athenna/ioc';
11
12
  import { Options, String } from '@athenna/common';
12
- import { debug } from '#src/debug/index';
13
13
  /**
14
14
  * Create a middleware inside the service provider.
15
15
  */
@@ -17,24 +17,24 @@ export function Middleware(options) {
17
17
  return (target) => {
18
18
  options = Options.create(options, {
19
19
  isGlobal: false,
20
- name: String.toCamelCase(target.name),
21
- alias: `App/Http/Middlewares/${target.name}`,
22
20
  type: 'transient',
21
+ alias: `App/Http/Middlewares/${target.name}`,
22
+ name: String.toCamelCase(target.name)
23
+ });
24
+ options.name = `App/Http/Middlewares/Names/${options.name}`;
25
+ debug('Registering middleware metadata for the service container %o', {
26
+ ...options,
27
+ name: target.name,
28
+ namedAlias: options.name
23
29
  });
24
- const alias = options.alias;
25
- const createCamelAlias = false;
26
- if (ioc.hasDependency(alias)) {
27
- debug('Middleware %s was already registered in the service container. Skipping registration via Middleware annotation.', alias);
30
+ if (ioc.has(options.name)) {
31
+ debug('Skipping registration, named alias %s is already registered.', options.name);
28
32
  return;
29
33
  }
30
- ioc[options.type](alias, target, createCamelAlias);
31
- Reflect.defineMetadata('ioc:registered', true, target);
32
- if (!options.isGlobal) {
33
- ioc.alias(`App/Http/Middlewares/Names/${options.name}`, alias);
34
+ if (ioc.has(options.alias)) {
35
+ debug('Skipping registration, alias %s is already registered.', options.alias);
34
36
  return;
35
37
  }
36
- const Middleware = ioc.safeUse(alias);
37
- debug('Registering %s as a global middleware via Middleware annotation.', Middleware.constructor.name);
38
- Server.middleware(Middleware.handle);
38
+ Annotation.defineMeta(target, options);
39
39
  };
40
40
  }
@@ -7,9 +7,9 @@
7
7
  * file that was distributed with this source code.
8
8
  */
9
9
  import 'reflect-metadata';
10
- import { Server } from '#src/facades/Server';
10
+ import { debug } from '#src/debug';
11
+ import { Annotation } from '@athenna/ioc';
11
12
  import { Options, String } from '@athenna/common';
12
- import { debug } from '#src/debug/index';
13
13
  /**
14
14
  * Create a terminator inside the service provider.
15
15
  */
@@ -17,24 +17,24 @@ export function Terminator(options) {
17
17
  return (target) => {
18
18
  options = Options.create(options, {
19
19
  isGlobal: false,
20
- name: String.toCamelCase(target.name),
21
- alias: `App/Http/Terminators/${target.name}`,
22
20
  type: 'transient',
21
+ alias: `App/Http/Terminators/${target.name}`,
22
+ name: String.toCamelCase(target.name)
23
+ });
24
+ options.name = `App/Http/Terminators/Names/${options.name}`;
25
+ debug('Registering terminator metadata for the service container %o', {
26
+ ...options,
27
+ name: target.name,
28
+ namedAlias: options.name
23
29
  });
24
- const alias = options.alias;
25
- const createCamelAlias = false;
26
- if (ioc.hasDependency(alias)) {
27
- debug('Terminator %s was already registered in the service container. Skipping registration via Terminator annotation.', alias);
30
+ if (ioc.has(options.name)) {
31
+ debug('Skipping registration, named alias %s is already registered.', options.name);
28
32
  return;
29
33
  }
30
- ioc[options.type](alias, target, createCamelAlias);
31
- Reflect.defineMetadata('ioc:registered', true, target);
32
- if (!options.isGlobal) {
33
- ioc.alias(`App/Http/Terminators/Names/${options.name}`, alias);
34
+ if (ioc.has(options.alias)) {
35
+ debug('Skipping registration, alias %s is already registered.', options.alias);
34
36
  return;
35
37
  }
36
- const Terminator = ioc.safeUse(alias);
37
- debug('Registering %s as a global terminator via Terminator annotation.', Terminator.constructor.name);
38
- Server.middleware(Terminator.terminate);
38
+ Annotation.defineMeta(target, options);
39
39
  };
40
40
  }
@@ -63,6 +63,6 @@ export class MakeControllerCommand extends BaseCommand {
63
63
  }
64
64
  __decorate([
65
65
  Argument({
66
- description: 'The controller name.',
66
+ description: 'The controller name.'
67
67
  })
68
68
  ], MakeControllerCommand.prototype, "name", void 0);
@@ -63,6 +63,6 @@ export class MakeInterceptorCommand extends BaseCommand {
63
63
  }
64
64
  __decorate([
65
65
  Argument({
66
- description: 'The interceptor name.',
66
+ description: 'The interceptor name.'
67
67
  })
68
68
  ], MakeInterceptorCommand.prototype, "name", void 0);
@@ -63,6 +63,6 @@ export class MakeMiddlewareCommand extends BaseCommand {
63
63
  }
64
64
  __decorate([
65
65
  Argument({
66
- description: 'The middleware name.',
66
+ description: 'The middleware name.'
67
67
  })
68
68
  ], MakeMiddlewareCommand.prototype, "name", void 0);
@@ -63,6 +63,6 @@ export class MakeTerminatorCommand extends BaseCommand {
63
63
  }
64
64
  __decorate([
65
65
  Argument({
66
- description: 'The terminator name.',
66
+ description: 'The terminator name.'
67
67
  })
68
68
  ], MakeTerminatorCommand.prototype, "name", void 0);
@@ -36,7 +36,7 @@ export class RouteListCommand extends BaseCommand {
36
36
  route.methods.join('|'),
37
37
  route.url,
38
38
  route.name || 'Not found',
39
- route.handler.name || 'closure',
39
+ route.handler.name || 'closure'
40
40
  ]);
41
41
  });
42
42
  table.render();
@@ -20,7 +20,7 @@ export class HttpException extends Exception {
20
20
  options = Options.create(options, {
21
21
  status: 500,
22
22
  code: 'E_HTTP_ERROR',
23
- message: 'An http exception has ocurred.',
23
+ message: 'An http exception has ocurred.'
24
24
  });
25
25
  super(options);
26
26
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @athenna/ioc
3
+ *
4
+ * (c) João Lenon <lenon@athenna.io>
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import { Exception } from '@athenna/common';
10
+ export declare class NotFoundControllerException extends Exception {
11
+ constructor(alias: string);
12
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @athenna/ioc
3
+ *
4
+ * (c) João Lenon <lenon@athenna.io>
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import { Exception } from '@athenna/common';
10
+ export class NotFoundControllerException extends Exception {
11
+ constructor(alias) {
12
+ super({
13
+ status: 500,
14
+ code: 'E_NOT_FOUND_CONTROLLER_ERROR',
15
+ message: `The controller with ${alias} alias has not been found inside the container.`,
16
+ help: `Remember to register the controller in your .athennarc.json file.`
17
+ });
18
+ }
19
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @athenna/ioc
3
+ *
4
+ * (c) João Lenon <lenon@athenna.io>
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import { Exception } from '@athenna/common';
10
+ export declare class NotFoundMiddlewareException extends Exception {
11
+ constructor(alias: string, namedAlias: string);
12
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @athenna/ioc
3
+ *
4
+ * (c) João Lenon <lenon@athenna.io>
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import { Exception } from '@athenna/common';
10
+ export class NotFoundMiddlewareException extends Exception {
11
+ constructor(alias, namedAlias) {
12
+ super({
13
+ status: 500,
14
+ code: 'E_NOT_FOUND_MIDDLEWARE_ERROR',
15
+ message: `The middleware with ${namedAlias} named alias and ${alias} alias has not been found inside the container.`,
16
+ help: `Remember to register the middleware in your .athennarc.json file.`
17
+ });
18
+ }
19
+ }
@@ -13,7 +13,7 @@ export class UndefinedMethodException extends Exception {
13
13
  status: 500,
14
14
  code: 'E_UNDEFINED_METHOD',
15
15
  message: `Cannot register the ${method} method in your Route because it's not defined inside your ${className} class.`,
16
- help: `Remember defining the method ${method} inside your ${className} class.`,
16
+ help: `Remember defining the method ${method} inside your ${className} class.`
17
17
  });
18
18
  }
19
19
  }
@@ -28,7 +28,7 @@ export class FastifyHandler {
28
28
  body: req.body,
29
29
  params: req.params,
30
30
  queries: req.query,
31
- headers: req.headers,
31
+ headers: req.headers
32
32
  });
33
33
  };
34
34
  }
@@ -57,7 +57,7 @@ export class FastifyHandler {
57
57
  body: res.body,
58
58
  params: req.params,
59
59
  queries: req.query,
60
- headers: req.headers,
60
+ headers: req.headers
61
61
  });
62
62
  res.body = payload;
63
63
  if (Is.Object(payload)) {
@@ -82,7 +82,7 @@ export class FastifyHandler {
82
82
  body: res.body || req.body,
83
83
  headers: res.getHeaders(),
84
84
  status: res.statusCode,
85
- responseTime: res.getResponseTime(),
85
+ responseTime: res.getResponseTime()
86
86
  });
87
87
  };
88
88
  }
@@ -101,7 +101,7 @@ export class FastifyHandler {
101
101
  params: req.params,
102
102
  queries: req.query,
103
103
  headers: req.headers,
104
- error,
104
+ error
105
105
  });
106
106
  };
107
107
  }
@@ -30,7 +30,7 @@ export class HttpExceptionHandler {
30
30
  code: String.toSnakeCase(error.code || error.name).toUpperCase(),
31
31
  name: Json.copy(error.name),
32
32
  message: Json.copy(error.message),
33
- stack: Json.copy(error.stack),
33
+ stack: Json.copy(error.stack)
34
34
  };
35
35
  if (error.help) {
36
36
  body.help = Json.copy(error.help);
@@ -74,4 +74,13 @@ export declare class HttpKernel {
74
74
  * Resolve the import path by meta URL and import it.
75
75
  */
76
76
  private resolvePath;
77
+ /**
78
+ * Register the controllers using the meta information
79
+ * defined by annotations.
80
+ */
81
+ private registerUsingMeta;
82
+ /**
83
+ * Get the configuration for the given key.
84
+ */
85
+ private getConfig;
77
86
  }
@@ -12,7 +12,8 @@ import { debug } from '#src/debug';
12
12
  import { Log } from '@athenna/logger';
13
13
  import { Config } from '@athenna/config';
14
14
  import { isAbsolute, resolve } from 'node:path';
15
- import { File, Exec, Is, Module } from '@athenna/common';
15
+ import { Annotation } from '@athenna/ioc';
16
+ import { File, Exec, Module, String } from '@athenna/common';
16
17
  import { HttpExceptionHandler } from '#src/handlers/HttpExceptionHandler';
17
18
  const corsPlugin = await Module.safeImport('@fastify/cors');
18
19
  const helmetPlugin = await Module.safeImport('@fastify/helmet');
@@ -25,26 +26,38 @@ export class HttpKernel {
25
26
  * Register the @fastify/cors plugin in the Http server.
26
27
  */
27
28
  async registerCors() {
29
+ if (Config.is('http.cors.enabled', false)) {
30
+ debug('Not able to register cors plugin. Set the http.cors.enabled configuration as true.');
31
+ return;
32
+ }
28
33
  if (!corsPlugin) {
29
34
  debug('Not able to register cors plugin. Install @fastify/cors package.');
30
35
  return;
31
36
  }
32
- await Server.plugin(corsPlugin, Config.get('http.cors'));
37
+ await Server.plugin(corsPlugin, this.getConfig('http.cors'));
33
38
  }
34
39
  /**
35
40
  * Register the @fastify/helmet plugin in the Http server.
36
41
  */
37
42
  async registerHelmet() {
43
+ if (Config.is('http.helmet.enabled', false)) {
44
+ debug('Not able to register helmet plugin. Set the http.helmet.enabled configuration as true.');
45
+ return;
46
+ }
38
47
  if (!helmetPlugin) {
39
48
  debug('Not able to register helmet plugin. Install @fastify/helmet package.');
40
49
  return;
41
50
  }
42
- await Server.plugin(helmetPlugin, Config.get('http.helmet'));
51
+ await Server.plugin(helmetPlugin, this.getConfig('http.helmet'));
43
52
  }
44
53
  /**
45
54
  * Register the @fastify/swagger plugin in the Http server.
46
55
  */
47
56
  async registerSwagger() {
57
+ if (Config.is('http.swagger.enabled', false)) {
58
+ debug('Not able to register swagger plugin. Set the http.swagger.enabled configuration as true.');
59
+ return;
60
+ }
48
61
  if (swaggerPlugin) {
49
62
  debug('Not able to register swagger plugin. Install @fastify/swagger package.');
50
63
  await Server.plugin(swaggerPlugin, Config.get('http.swagger.configurations'));
@@ -58,17 +71,26 @@ export class HttpKernel {
58
71
  * Register the @fastify/rate-limit plugin in the Http server.
59
72
  */
60
73
  async registerRateLimit() {
74
+ if (Config.is('http.rateLimit.enabled', false)) {
75
+ debug('Not able to register rate limit plugin. Set the http.rateLimit.enabled configuration as true.');
76
+ return;
77
+ }
61
78
  if (!rateLimitPlugin) {
62
79
  debug('Not able to register rate limit plugin. Install @fastify/rate-limit package.');
63
80
  return;
64
81
  }
65
- await Server.plugin(rateLimitPlugin, Config.get('http.rateLimit'));
82
+ await Server.plugin(rateLimitPlugin, this.getConfig('http.rateLimit'));
66
83
  }
67
84
  /**
68
85
  * Register the cls-rtracer plugin in the Http server.
69
86
  */
70
87
  async registerRTracer(trace) {
71
88
  if (trace === false) {
89
+ debug('Not able to register rTracer plugin. Set the trace option as true in your http server options.');
90
+ return;
91
+ }
92
+ if (trace === undefined && Config.is('http.rTracer.enabled', false)) {
93
+ debug('Not able to register rTracer plugin. Set the http.rTracer.enabled configuration as true.');
72
94
  return;
73
95
  }
74
96
  if (!rTracerPlugin) {
@@ -76,14 +98,13 @@ export class HttpKernel {
76
98
  return;
77
99
  }
78
100
  Server.middleware(async (ctx) => (ctx.data.traceId = rTracerPlugin.id()));
79
- await Server.plugin(rTracerPlugin.fastifyPlugin, Config.get('http.rTracer'));
101
+ await Server.plugin(rTracerPlugin.fastifyPlugin, this.getConfig('http.rTracer'));
80
102
  }
81
103
  /**
82
104
  * Register the global log terminator in the Http server.
83
105
  */
84
106
  async registerLoggerTerminator() {
85
- if (!Config.exists('http.logger.enabled') ||
86
- Config.is('http.logger.enabled', false)) {
107
+ if (Config.is('http.logger.enabled', false)) {
87
108
  debug('Not able to register http request logger. Enable it in your http.logger.enabled configuration.');
88
109
  return;
89
110
  }
@@ -97,13 +118,11 @@ export class HttpKernel {
97
118
  const controllers = Config.get('rc.controllers', []);
98
119
  await Exec.concurrently(controllers, async (path) => {
99
120
  const Controller = await this.resolvePath(path);
100
- if (Reflect.hasMetadata('ioc:registered', Controller)) {
101
- debug('Controller %s already registered by Controller annotation. Skipping registration via HttpKernel.', Controller.name);
121
+ if (Annotation.isAnnotated(Controller)) {
122
+ this.registerUsingMeta(Controller);
102
123
  return;
103
124
  }
104
- const createCamelAlias = false;
105
- const alias = `App/Http/Controllers/${Controller.name}`;
106
- ioc.bind(alias, Controller, createCamelAlias);
125
+ ioc.transient(`App/Http/Controllers/${Controller.name}`, Controller);
107
126
  });
108
127
  }
109
128
  /**
@@ -112,30 +131,34 @@ export class HttpKernel {
112
131
  * and "rc.globalMiddlewares" exists.
113
132
  */
114
133
  async registerMiddlewares() {
134
+ const middlewares = Config.get('rc.middlewares', []);
135
+ await Exec.concurrently(middlewares, async (path) => {
136
+ const Middleware = await this.resolvePath(path);
137
+ if (Annotation.isAnnotated(Middleware)) {
138
+ this.registerUsingMeta(Middleware);
139
+ return;
140
+ }
141
+ const alias = `App/Http/Middlewares/${Middleware.name}`;
142
+ const namedAlias = `App/Http/Middlewares/Names/${String.toCamelCase(Middleware.name)}`;
143
+ ioc.transient(alias, Middleware).alias(namedAlias, alias);
144
+ });
115
145
  await this.registerNamedMiddlewares();
116
146
  await this.registerGlobalMiddlewares();
117
- if (Config.exists('rc.middlewares')) {
118
- await Exec.concurrently(Config.get('rc.middlewares'), this.resolvePath);
119
- }
120
147
  }
121
148
  /**
122
149
  * Register all the named middlewares found inside "rc.namedMiddlewares"
123
150
  * property.
124
151
  */
125
152
  async registerNamedMiddlewares() {
126
- const namedMiddlewares = Config.get('rc.namedMiddlewares');
127
- if (Is.Empty(namedMiddlewares)) {
128
- return;
129
- }
153
+ const namedMiddlewares = Config.get('rc.namedMiddlewares', {});
130
154
  await Exec.concurrently(Object.keys(namedMiddlewares), async (key) => {
131
155
  const Middleware = await this.resolvePath(namedMiddlewares[key]);
132
- if (Reflect.hasMetadata('ioc:registered', Middleware)) {
133
- debug('Named middleware %s already registered by Middleware annotation. Skipping registration via HttpKernel.', Middleware.name);
156
+ if (Annotation.isAnnotated(Middleware)) {
157
+ this.registerUsingMeta(Middleware);
134
158
  return;
135
159
  }
136
- const createCamelAlias = false;
137
160
  const { alias, namedAlias } = this.getNamedMiddlewareAlias(key, Middleware);
138
- ioc.bind(alias, Middleware, createCamelAlias).alias(namedAlias, alias);
161
+ ioc.bind(alias, Middleware).alias(namedAlias, alias);
139
162
  });
140
163
  }
141
164
  /**
@@ -143,20 +166,19 @@ export class HttpKernel {
143
166
  * property.
144
167
  */
145
168
  async registerGlobalMiddlewares() {
146
- const globalMiddlewares = Config.get('rc.globalMiddlewares');
147
- if (Is.Empty(globalMiddlewares)) {
148
- return;
149
- }
169
+ const globalMiddlewares = Config.get('rc.globalMiddlewares', []);
150
170
  await Exec.concurrently(globalMiddlewares, async (path) => {
151
171
  const Middleware = await this.resolvePath(path);
152
- if (Reflect.hasMetadata('ioc:registered', Middleware)) {
153
- debug('Global middleware %s already registered by Middleware annotation. Skipping registration via HttpKernel.', Middleware.name);
172
+ if (Annotation.isAnnotated(Middleware)) {
173
+ this.registerUsingMeta(Middleware);
154
174
  return;
155
175
  }
156
- const createCamelAlias = false;
157
176
  const { alias, handler, serverMethod } = this.getGlobalMiddlewareAliasAndHandler(Middleware);
158
- ioc.bind(alias, Middleware, createCamelAlias);
159
- Server[serverMethod](ioc.safeUse(alias)[handler]);
177
+ ioc.bind(alias, Middleware);
178
+ Server[serverMethod]((...args) => {
179
+ const mid = ioc.safeUse(alias);
180
+ return mid[handler].bind(mid)(...args);
181
+ });
160
182
  });
161
183
  }
162
184
  /**
@@ -198,19 +220,19 @@ export class HttpKernel {
198
220
  if (middleware.handle) {
199
221
  return {
200
222
  alias: `App/Http/Middlewares/${Middleware.name}`,
201
- namedAlias: `App/Http/Middlewares/Names/${name}`,
223
+ namedAlias: `App/Http/Middlewares/Names/${name}`
202
224
  };
203
225
  }
204
226
  if (middleware.intercept) {
205
227
  return {
206
228
  alias: `App/Http/Interceptors/${Middleware.name}`,
207
- namedAlias: `App/Http/Interceptors/Names/${name}`,
229
+ namedAlias: `App/Http/Interceptors/Names/${name}`
208
230
  };
209
231
  }
210
232
  if (middleware.terminate) {
211
233
  return {
212
234
  alias: `App/Http/Terminators/${Middleware.name}`,
213
- namedAlias: `App/Http/Terminators/Names/${name}`,
235
+ namedAlias: `App/Http/Terminators/Names/${name}`
214
236
  };
215
237
  }
216
238
  }
@@ -224,21 +246,21 @@ export class HttpKernel {
224
246
  return {
225
247
  handler: 'handle',
226
248
  serverMethod: 'middleware',
227
- alias: `App/Http/Middlewares/${Middleware.name}`,
249
+ alias: `App/Http/Middlewares/${Middleware.name}`
228
250
  };
229
251
  }
230
252
  if (middleware.intercept) {
231
253
  return {
232
254
  handler: 'intercept',
233
255
  serverMethod: 'intercept',
234
- alias: `App/Http/Interceptors/${Middleware.name}`,
256
+ alias: `App/Http/Interceptors/${Middleware.name}`
235
257
  };
236
258
  }
237
259
  if (middleware.terminate) {
238
260
  return {
239
261
  handler: 'terminate',
240
262
  serverMethod: 'terminate',
241
- alias: `App/Http/Terminators/${Middleware.name}`,
263
+ alias: `App/Http/Terminators/${Middleware.name}`
242
264
  };
243
265
  }
244
266
  }
@@ -248,4 +270,36 @@ export class HttpKernel {
248
270
  resolvePath(path) {
249
271
  return Module.resolve(`${path}?version=${Math.random()}`, Config.get('rc.meta'));
250
272
  }
273
+ /**
274
+ * Register the controllers using the meta information
275
+ * defined by annotations.
276
+ */
277
+ registerUsingMeta(target) {
278
+ const meta = Annotation.getMeta(target);
279
+ ioc[meta.type](meta.alias, target);
280
+ if (meta.name && !meta.isGlobal) {
281
+ ioc.alias(meta.name, meta.alias);
282
+ }
283
+ if (meta.camelAlias) {
284
+ ioc.alias(meta.camelAlias, meta.alias);
285
+ }
286
+ if (meta.isGlobal) {
287
+ const { handler, serverMethod } = this.getGlobalMiddlewareAliasAndHandler(target);
288
+ Server[serverMethod]((...args) => {
289
+ const mid = ioc.safeUse(meta.alias);
290
+ return mid[handler].bind(mid)(...args);
291
+ });
292
+ }
293
+ return meta;
294
+ }
295
+ /**
296
+ * Get the configuration for the given key.
297
+ */
298
+ getConfig(key) {
299
+ const config = Config.get(key);
300
+ if (Config.exists(`${key}.enabled`)) {
301
+ delete config.enabled;
302
+ }
303
+ return config;
304
+ }
251
305
  }
@@ -10,6 +10,6 @@ import { Router } from '#src/router/Router';
10
10
  import { ServiceProvider } from '@athenna/ioc';
11
11
  export class HttpRouteProvider extends ServiceProvider {
12
12
  register() {
13
- this.container.instance('Athenna/Core/HttpRoute', new Router(), false);
13
+ this.container.singleton('Athenna/Core/HttpRoute', Router);
14
14
  }
15
15
  }
@@ -10,7 +10,7 @@ import { ServiceProvider } from '@athenna/ioc';
10
10
  import { ServerImpl } from '#src/server/ServerImpl';
11
11
  export class HttpServerProvider extends ServiceProvider {
12
12
  register() {
13
- this.container.instance('Athenna/Core/HttpServer', new ServerImpl(), false);
13
+ this.container.instance('Athenna/Core/HttpServer', new ServerImpl());
14
14
  }
15
15
  async shutdown() {
16
16
  const Server = this.container.use('Athenna/Core/HttpServer');
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { Is, Route as RouteHelper } from '@athenna/common';
10
10
  import { UndefinedMethodException } from '#src/exceptions/UndefinedMethodException';
11
+ import { NotFoundMiddlewareException } from '#src/exceptions/NotFoundMiddlewareException';
11
12
  export class Route {
12
13
  constructor(url, methods, handler) {
13
14
  this.route = {
@@ -18,9 +19,9 @@ export class Route {
18
19
  middlewares: {
19
20
  middlewares: [],
20
21
  terminators: [],
21
- interceptors: [],
22
+ interceptors: []
22
23
  },
23
- fastify: { schema: {} },
24
+ fastify: { schema: {} }
24
25
  };
25
26
  if (Is.String(handler)) {
26
27
  const [controller, method] = handler.split('.');
@@ -28,7 +29,13 @@ export class Route {
28
29
  if (!dependency[method]) {
29
30
  throw new UndefinedMethodException(method, controller);
30
31
  }
31
- this.route.handler = dependency[method].bind(dependency);
32
+ this.route.handler = (...args) => {
33
+ const service = ioc.safeUse(`App/Http/Controllers/${controller}`);
34
+ if (!service[method]) {
35
+ throw new UndefinedMethodException(method, controller);
36
+ }
37
+ return service[method].bind(dependency)(...args);
38
+ };
32
39
  }
33
40
  else {
34
41
  this.route.handler = handler;
@@ -58,7 +65,7 @@ export class Route {
58
65
  vanillaOptions(options) {
59
66
  this.route.fastify = {
60
67
  ...this.route.fastify,
61
- ...options,
68
+ ...options
62
69
  };
63
70
  return this;
64
71
  }
@@ -81,9 +88,15 @@ export class Route {
81
88
  middleware(middleware, prepend = false) {
82
89
  const insertionType = prepend ? 'unshift' : 'push';
83
90
  if (Is.String(middleware)) {
84
- const mid = ioc.use(`App/Http/Middlewares/Names/${middleware}`) ||
85
- ioc.safeUse(`App/Http/Middlewares/${middleware}`);
86
- this.route.middlewares.middlewares[insertionType](mid.handle.bind(mid));
91
+ const namedAlias = `App/Http/Middlewares/Names/${middleware}`;
92
+ const alias = `App/Http/Middlewares/${middleware}`;
93
+ if (!ioc.has(namedAlias) && !ioc.has(alias)) {
94
+ throw new NotFoundMiddlewareException(alias, namedAlias);
95
+ }
96
+ this.route.middlewares.middlewares[insertionType]((...args) => {
97
+ const mid = ioc.use(namedAlias) || ioc.safeUse(alias);
98
+ return mid.handle.bind(mid)(...args);
99
+ });
87
100
  return this;
88
101
  }
89
102
  if (Is.Function(middleware)) {
@@ -100,9 +113,15 @@ export class Route {
100
113
  interceptor(interceptor, prepend = false) {
101
114
  const insertionType = prepend ? 'unshift' : 'push';
102
115
  if (Is.String(interceptor)) {
103
- const inte = ioc.use(`App/Http/Interceptors/Names/${interceptor}`) ||
104
- ioc.safeUse(`App/Http/Interceptors/${interceptor}`);
105
- this.route.middlewares.interceptors[insertionType](inte.intercept.bind(inte));
116
+ const namedAlias = `App/Http/Interceptors/Names/${interceptor}`;
117
+ const alias = `App/Http/Interceptors/${interceptor}`;
118
+ if (!ioc.has(namedAlias) && !ioc.has(alias)) {
119
+ throw new NotFoundMiddlewareException(alias, namedAlias);
120
+ }
121
+ this.route.middlewares.interceptors[insertionType]((...args) => {
122
+ const mid = ioc.use(namedAlias) || ioc.safeUse(alias);
123
+ return mid.intercept.bind(mid)(...args);
124
+ });
106
125
  return this;
107
126
  }
108
127
  if (Is.Function(interceptor)) {
@@ -119,9 +138,15 @@ export class Route {
119
138
  terminator(terminator, prepend = false) {
120
139
  const insertionType = prepend ? 'unshift' : 'push';
121
140
  if (Is.String(terminator)) {
122
- const ter = ioc.use(`App/Http/Terminators/Names/${terminator}`) ||
123
- ioc.safeUse(`App/Http/Terminators/${terminator}`);
124
- this.route.middlewares.terminators[insertionType](ter.terminate.bind(ter));
141
+ const namedAlias = `App/Http/Terminators/Names/${terminator}`;
142
+ const alias = `App/Http/Terminators/${terminator}`;
143
+ if (!ioc.has(namedAlias) && !ioc.has(alias)) {
144
+ throw new NotFoundMiddlewareException(alias, namedAlias);
145
+ }
146
+ this.route.middlewares.terminators[insertionType]((...args) => {
147
+ const mid = ioc.use(namedAlias) || ioc.safeUse(alias);
148
+ return mid.terminate.bind(mid)(...args);
149
+ });
125
150
  return this;
126
151
  }
127
152
  if (Is.Function(terminator)) {
@@ -262,7 +287,7 @@ export class Route {
262
287
  if (!this.route.fastify.schema.body) {
263
288
  this.route.fastify.schema.body = {
264
289
  type: 'object',
265
- properties: {},
290
+ properties: {}
266
291
  };
267
292
  }
268
293
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -282,7 +307,7 @@ export class Route {
282
307
  if (!this.route.fastify.schema.params) {
283
308
  this.route.fastify.schema.params = {
284
309
  type: 'object',
285
- properties: {},
310
+ properties: {}
286
311
  };
287
312
  }
288
313
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -302,7 +327,7 @@ export class Route {
302
327
  if (!this.route.fastify.schema.querystring) {
303
328
  this.route.fastify.schema.querystring = {
304
329
  type: 'object',
305
- properties: {},
330
+ properties: {}
306
331
  };
307
332
  }
308
333
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -371,7 +396,7 @@ export class Route {
371
396
  if (!this.route.fastify.schema.externalDocs) {
372
397
  this.route.fastify.schema.externalDocs = {
373
398
  url: '',
374
- description: '',
399
+ description: ''
375
400
  };
376
401
  }
377
402
  this.route.fastify.schema.externalDocs.url = url;
@@ -419,7 +444,7 @@ export class Route {
419
444
  prefixes: this.route.prefixes,
420
445
  handler: this.route.handler,
421
446
  middlewares: this.route.middlewares,
422
- fastify: this.route.fastify,
447
+ fastify: this.route.fastify
423
448
  };
424
449
  }
425
450
  getUrl() {
@@ -136,7 +136,7 @@ export class ServerImpl {
136
136
  options.middlewares = Options.create(options.middlewares, {
137
137
  middlewares: [],
138
138
  terminators: [],
139
- interceptors: [],
139
+ interceptors: []
140
140
  });
141
141
  if (options.methods.length === 2 && options.methods.includes('HEAD')) {
142
142
  this.route({ ...options, methods: ['GET'] });
@@ -145,8 +145,8 @@ export class ServerImpl {
145
145
  methods: ['HEAD'],
146
146
  fastify: {
147
147
  ...options.fastify,
148
- schema: { ...options.fastify.schema, hide: true },
149
- },
148
+ schema: { ...options.fastify.schema, hide: true }
149
+ }
150
150
  });
151
151
  return;
152
152
  }
@@ -159,7 +159,7 @@ export class ServerImpl {
159
159
  preHandler: options.middlewares.middlewares.map(m => FastifyHandler.handle(m)),
160
160
  onSend: options.middlewares.interceptors.map(m => FastifyHandler.intercept(m)),
161
161
  onResponse: options.middlewares.terminators.map(m => FastifyHandler.terminate(m)),
162
- ...options.fastify,
162
+ ...options.fastify
163
163
  });
164
164
  }
165
165
  /**
@@ -8,18 +8,26 @@
8
8
  */
9
9
  export type ControllerOptions = {
10
10
  /**
11
- * The alias that will be used to register the dependency inside
12
- * the service provider. Athenna will not create camel alias from
13
- * the alias set here.
11
+ * The alias that will be used to register the controller inside
12
+ * the service container.
14
13
  *
15
14
  * @default App/Http/Controllers/YourControllerClassName
16
15
  */
17
16
  alias?: string;
17
+ /**
18
+ * The camel alias that will be used as an alias of the real
19
+ * controller alias. Camel alias is important when you want to
20
+ * work with constructor injection. By default, Athenna doesn't
21
+ * create camel alias for controllers.
22
+ *
23
+ * @default undefined
24
+ */
25
+ camelAlias?: string;
18
26
  /**
19
27
  * The registration type that will be used to register your controller
20
- * inside the service provider.
28
+ * inside the service container.
21
29
  *
22
- * @default transient
30
+ * @default 'transient'
23
31
  */
24
32
  type?: 'fake' | 'scoped' | 'singleton' | 'transient';
25
33
  };
@@ -8,18 +8,26 @@
8
8
  */
9
9
  export type MiddlewareOptions = {
10
10
  /**
11
- * The alias that will be used to register the dependency inside
12
- * the service provider. Athenna will not create camel alias from
13
- * the alias set here.
11
+ * The alias that will be used to register the middleware inside
12
+ * the service container.
14
13
  *
15
14
  * @default App/Http/Middlewares/YourMiddlewareClassName
16
15
  */
17
16
  alias?: string;
17
+ /**
18
+ * The camel alias that will be used as an alias of the real
19
+ * middleware alias. Camel alias is important when you want to
20
+ * work with constructor injection. By default, Athenna doesn't
21
+ * create camel alias for middlewares.
22
+ *
23
+ * @default undefined
24
+ */
25
+ camelAlias?: string;
18
26
  /**
19
27
  * The registration type that will be used to register your middleware
20
- * inside the service provider.
28
+ * inside the service container.
21
29
  *
22
- * @default transient
30
+ * @default 'transient'
23
31
  */
24
32
  type?: 'fake' | 'scoped' | 'singleton' | 'transient';
25
33
  /**
@@ -34,7 +42,7 @@ export type MiddlewareOptions = {
34
42
  * is true, Athenna will ignore this property. Athenna will always set the default
35
43
  * name of your middleware as the middleware class name in camel case format.
36
44
  *
37
- * @default yourMiddlewareClassName
45
+ * @default 'yourMiddlewareClassName'
38
46
  */
39
47
  name?: string;
40
48
  };