Skip to content

Commit 46bd3db

Browse files
author
hirsch88
committed
add recursive controllers, services and co
1 parent 21ae846 commit 46bd3db

File tree

9 files changed

+194
-19
lines changed

9 files changed

+194
-19
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"@types/dotenv": "^4.0.0",
6262
"@types/express": "^4.0.35",
6363
"@types/faker": "^4.1.0",
64+
"@types/glob": "^5.0.30",
6465
"@types/helmet": "0.0.35",
6566
"@types/jest": "^19.2.3",
6667
"@types/jsonwebtoken": "^7.2.0",
@@ -85,6 +86,7 @@
8586
"express": "^4.15.3",
8687
"express-status-monitor": "^0.1.9",
8788
"faker": "^4.1.0",
89+
"glob": "^7.1.2",
8890
"helmet": "^3.6.1",
8991
"inversify": "^4.1.1",
9092
"inversify-express-utils": "^3.5.1",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { injectable } from 'inversify';
2+
3+
4+
export namespace auth {
5+
6+
@injectable()
7+
export class AuthController {
8+
9+
}
10+
}

src/constants/Targets.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ export const Core = {
1616
};
1717

1818
export const Controller = {
19-
UserController: 'UserController'
19+
UserController: 'UserController',
20+
auth: {
21+
AuthController: 'auth.AuthController'
22+
}
2023
};
2124

2225
export const Service = {

src/core/ApiInfo.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as express from 'express';
2+
import { my } from 'my-express';
3+
import { Environment } from './Environment';
4+
5+
6+
export class ApiInfo {
7+
8+
constructor(public app: express.Application) { }
9+
10+
public setup(): void {
11+
if (Environment.get<string>('API_INFO_ENABLED') === 'true') {
12+
this.app.get(Environment.get<string>('APP_URL_PREFIX') + Environment.get<string>('API_INFO_ROUTE'), (req: my.Request, res: my.Response) => {
13+
const pkg = Environment.getPkg();
14+
const links = {
15+
links: {}
16+
};
17+
if (Environment.get<string>('SWAGGER_ENABLED') === 'true') {
18+
links.links['swagger'] = `${this.app.get('host')}:${this.app.get('port')}${process.env.APP_URL_PREFIX}${process.env.SWAGGER_ROUTE}`;
19+
}
20+
if (Environment.get<string>('MONITOR_ENABLED') === 'true') {
21+
links.links['monitor'] = `${this.app.get('host')}:${this.app.get('port')}${process.env.APP_URL_PREFIX}${process.env.MONITOR_ROUTE}`;
22+
}
23+
return res.json({
24+
name: pkg.name,
25+
version: pkg.version,
26+
description: pkg.description,
27+
...links
28+
});
29+
});
30+
}
31+
}
32+
33+
}

src/core/ApiMonitor.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as express from 'express';
2+
import * as monitor from 'express-status-monitor';
3+
import { my } from 'my-express';
4+
import { Environment } from './Environment';
5+
6+
7+
export class ApiMonitor {
8+
9+
constructor(public app: express.Application) { }
10+
11+
public setup(): void {
12+
if (Environment.get<string>('MONITOR_ENABLED') === 'true') {
13+
this.app.use(monitor());
14+
this.app.get(Environment.get<string>('APP_URL_PREFIX') + Environment.get<string>('MONITOR_ROUTE'), monitor().pageRoute);
15+
}
16+
}
17+
18+
}

src/core/Bootstrap.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import { events } from './api/events';
2222
import { Server } from './Server';
2323
import { ioc } from './IoC';
2424
import { Log } from './log';
25+
import { ApiInfo } from './ApiInfo';
26+
import { SwaggerUI } from './SwaggerUI';
27+
import { ApiMonitor } from './ApiMonitor';
28+
2529
import '../container';
2630

2731
const log = new Log('core:Bootstrap');
@@ -53,10 +57,12 @@ export class Bootstrap {
5357

5458
public async main(): Promise<void> {
5559
log.info('Configuring app...');
60+
this.setupMonitor();
5661
this.app = this.appConfiguration(this.app);
5762

5863
await this.bindIoC();
5964
this.setupIoC();
65+
this.setupCoreTools();
6066
this.startServer();
6167
}
6268

@@ -82,7 +88,25 @@ export class Bootstrap {
8288
}, this.app);
8389
this.inversifyExpressServer.setConfig((a) => a.use(extendExpressResponse));
8490
this.inversifyExpressServer.setErrorConfig((a) => a.use(exceptionHandler));
85-
this.app = this.inversifyExpressServer.build();
91+
try {
92+
this.app = this.inversifyExpressServer.build();
93+
} catch (e) {
94+
log.error(e.message);
95+
process.exit(1);
96+
}
97+
}
98+
99+
private setupMonitor(): void {
100+
const apiMonitor = new ApiMonitor(this.app);
101+
apiMonitor.setup();
102+
}
103+
104+
private setupCoreTools(): void {
105+
const apiInfo = new ApiInfo(this.app);
106+
apiInfo.setup();
107+
108+
const swaggerUI = new SwaggerUI(this.app);
109+
swaggerUI.setup();
86110
}
87111

88112
private startServer(): void {

src/core/IoC.ts

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
import * as fs from 'fs';
10+
import * as glob from 'glob';
1011
import { interfaces } from 'inversify-express-utils';
1112
import { Container } from 'inversify';
1213
import { Types } from '../constants/Types';
@@ -37,10 +38,11 @@ class IoC {
3738

3839
public async bindModules(): Promise<void> {
3940
this.bindCore();
41+
await this.bindControllers();
42+
4043
await this.bindModels();
4144
await this.bindRepositories();
4245
await this.bindServices();
43-
await this.bindControllers();
4446

4547
this.container = this.customConfiguration(this.container);
4648
}
@@ -51,7 +53,7 @@ class IoC {
5153
}
5254

5355
private bindModels(): Promise<void> {
54-
return this.bindFiles('/models', Model, (name: any, value: any) => {
56+
return this.bindFiles('/models/**/*.ts', Model, (name: any, value: any) => {
5557
this.container
5658
.bind<any>(Types.Model)
5759
.toConstantValue(value)
@@ -60,7 +62,7 @@ class IoC {
6062
}
6163

6264
private bindRepositories(): Promise<void> {
63-
return this.bindFiles('/repositories', Repository, (name: any, value: any) => {
65+
return this.bindFiles('/repositories/**/*Repository.ts', Repository, (name: any, value: any) => {
6466
this.container
6567
.bind<any>(Types.Repository)
6668
.to(value)
@@ -69,7 +71,7 @@ class IoC {
6971
}
7072

7173
private bindServices(): Promise<void> {
72-
return this.bindFiles('/services', Service, (name: any, value: any) => {
74+
return this.bindFiles('/services/**/*Service.ts', Service, (name: any, value: any) => {
7375
this.container
7476
.bind<any>(Types.Service)
7577
.to(value)
@@ -78,7 +80,7 @@ class IoC {
7880
}
7981

8082
private bindControllers(): Promise<void> {
81-
return this.bindFiles('/controllers', Controller, (name: any, value: any) => {
83+
return this.bindFiles('/controllers/**/*Controller.ts', Controller, (name: any, value: any) => {
8284
this.container
8385
.bind<any>(Types.Controller)
8486
.to(value)
@@ -90,9 +92,10 @@ class IoC {
9092
return new Promise<void>((resolve, reject) => {
9193
this.getFiles(path, (files: string[]) => {
9294
files.forEach((file: any) => {
93-
let fileExport;
95+
let fileExport, fileClass, fileTarget;
96+
const isRecursive = file.name.indexOf('.') > 0;
9497
try {
95-
fileExport = require(`${file.path}/${file.fileName}`);
98+
fileExport = require(`${file.path}`);
9699
} catch (e) {
97100
log.warn(e.message);
98101
return;
@@ -101,45 +104,84 @@ class IoC {
101104
log.warn(`Could not find the file ${file.name}!`);
102105
return;
103106
}
104-
if (fileExport[file.name] === undefined) {
107+
if (isRecursive) {
108+
fileClass = this.getClassOfFileExport(file.name, fileExport);
109+
fileTarget = this.getTargetOfFile(file.name, target);
110+
111+
} else {
112+
fileClass = fileExport[file.name];
113+
fileTarget = target && target[file.name];
114+
}
115+
116+
if (fileClass === undefined) {
105117
log.warn(`Name of the file '${file.name}' does not match to the class name!`);
106118
return;
107119
}
108-
if (target && target[file.name] === undefined) {
120+
121+
if (fileTarget === undefined) {
109122
log.warn(`Please define your '${file.name}' class is in the target constants.`);
110123
return;
111124
}
112-
callback(target[file.name], fileExport[file.name]);
125+
126+
callback(fileTarget, fileClass);
113127
});
114128
resolve();
115129
});
116130
});
117131
}
118132

133+
private getClassOfFileExport(name: string, fileExport: any): any {
134+
const fileParts = name.split('.');
135+
let fileClass = fileExport;
136+
fileParts.forEach((part) => {
137+
fileClass = fileClass[part];
138+
});
139+
return fileClass;
140+
}
141+
142+
private getTargetOfFile(name: string, target: any): any {
143+
const fileParts = name.split('.');
144+
let fileTarget = target;
145+
fileParts.forEach((part) => {
146+
fileTarget = fileTarget[part];
147+
});
148+
return fileTarget;
149+
}
150+
119151
private getBasePath(): string {
120152
const baseFolder = __dirname.indexOf('/src/') >= 0 ? '/src/' : '/dist/';
121153
const baseRoot = __dirname.substring(0, __dirname.indexOf(baseFolder));
122154
return `${baseRoot}${baseFolder}api`;
123155
}
124156

125157
private getFiles(path: string, done: (files: any[]) => void): void {
126-
fs.readdir(this.getBasePath() + path, (err: any, files: string[]): void => {
158+
glob(this.getBasePath() + path, (err: any, files: string[]) => {
127159
if (err) {
128160
log.warn(`Could not read the folder ${path}!`);
129161
return;
130162
}
131-
done(files.map((fileName: string) => ({
132-
path: this.getBasePath() + path,
133-
fileName: fileName,
134-
name: this.parseName(fileName)
135-
})));
163+
done(files.map((p: string) => this.parseFilePath(p)));
136164
});
137165
}
138166

139167
private parseName(fileName: string): string {
140168
return fileName.substring(0, fileName.length - 3);
141169
}
142170

171+
private parseFilePath(path: string): any {
172+
const filePath = path.substring(this.getBasePath().length + 1);
173+
const dir = filePath.split('/')[0];
174+
const file = filePath.substr(dir.length + 1);
175+
const name = file.replace('/', '.').substring(0, file.length - 3);
176+
return {
177+
path: path,
178+
filePath: filePath,
179+
dir: dir,
180+
file: file,
181+
name: name
182+
};
183+
}
184+
143185
}
144186

145187
export const ioc = new IoC();

src/core/SwaggerUI.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as express from 'express';
2+
import * as swaggerUi from 'swagger-ui-express';
3+
import { my } from 'my-express';
4+
import { Environment } from './Environment';
5+
6+
7+
export class SwaggerUI {
8+
9+
constructor(public app: express.Application) { }
10+
11+
public setup(): void {
12+
if (Environment.get<string>('SWAGGER_ENABLED') === 'true') {
13+
const baseFolder = __dirname.indexOf('/src/') >= 0 ? '/src/' : '/dist/';
14+
const basePath = __dirname.substring(0, __dirname.indexOf(baseFolder));
15+
const swaggerFile = require(basePath + Environment.get<string>('SWAGGER_FILE'));
16+
const packageJson = require(basePath + '/package.json');
17+
18+
// Add npm infos to the swagger doc
19+
swaggerFile.info = {
20+
title: packageJson.name,
21+
description: packageJson.description,
22+
version: packageJson.version
23+
};
24+
25+
// Initialize swagger-jsdoc -> returns validated swagger spec in json format
26+
// const swaggerUi = require('swagger-ui-express');
27+
const route = Environment.get<string>('APP_URL_PREFIX') + Environment.get<string>('SWAGGER_ROUTE');
28+
this.app.use(route, swaggerUi.serve, swaggerUi.setup(swaggerFile));
29+
}
30+
}
31+
32+
}

yarn.lock

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@
7171
dependencies:
7272
"@types/node" "*"
7373

74+
"@types/glob@^5.0.30":
75+
version "5.0.30"
76+
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.30.tgz#1026409c5625a8689074602808d082b2867b8a51"
77+
dependencies:
78+
"@types/minimatch" "*"
79+
"@types/node" "*"
80+
7481
"@types/helmet@0.0.35":
7582
version "0.0.35"
7683
resolved "https://registry.yarnpkg.com/@types/helmet/-/helmet-0.0.35.tgz#b5f035063180c2c1f43122aba2612c9ed9466dc0"
@@ -102,6 +109,10 @@
102109
version "0.0.29"
103110
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b"
104111

112+
"@types/minimatch@*":
113+
version "2.0.29"
114+
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a"
115+
105116
"@types/morgan@^1.7.32":
106117
version "1.7.32"
107118
resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.7.32.tgz#fab1ece4dae172e1a377d563d33e3634fa04927d"
@@ -1501,7 +1512,7 @@ glob-parent@^2.0.0:
15011512
dependencies:
15021513
is-glob "^2.0.0"
15031514

1504-
glob@^7.0.3, glob@^7.0.5, glob@^7.1.1:
1515+
glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
15051516
version "7.1.2"
15061517
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
15071518
dependencies:

0 commit comments

Comments
 (0)