1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | require("reflect-metadata");
|
4 | const injection_js_1 = require("injection-js");
|
5 | const YAML = require("yamljs");
|
6 | const express = require("express");
|
7 | const logger = require("morgan");
|
8 | const bodyParser = require("body-parser");
|
9 | const cookieParser = require("cookie-parser");
|
10 | const http = require("http");
|
11 | const Promise = require("bluebird");
|
12 | const knex = require("knex");
|
13 | const objection_1 = require("objection");
|
14 | const lodash_1 = require("lodash");
|
15 | const winston = require("winston");
|
16 | const logger_1 = require("./logger");
|
17 | class ParkaApp {
|
18 | constructor(ConfigConstructor) {
|
19 | this.ConfigConstructor = ConfigConstructor;
|
20 | this.configFile = process.env.PARKA_CONFIG_FILE;
|
21 | process.nextTick(() => {
|
22 | ParkaApp.appInstance = this;
|
23 | this.parseAppConfig().then(() => {
|
24 | this.configureDatabaseConneciton();
|
25 | this.configureExpressServer();
|
26 | lodash_1.forEach(this['__resourceClasses'], (ResourceClass) => {
|
27 | this.registerResource(ResourceClass);
|
28 | });
|
29 | this.onBeforeApplicationStart();
|
30 | this.configureGlobalErrorHandling();
|
31 | this.configureInjector();
|
32 | this.start();
|
33 | });
|
34 | });
|
35 | }
|
36 | onBeforeApplicationStart() {
|
37 | }
|
38 | registerResource(ResourceClass) {
|
39 | this.registerGetRoutes(ResourceClass);
|
40 | this.registerPostRoutes(ResourceClass);
|
41 | this.registerPutRoutes(ResourceClass);
|
42 | this.registerDeleteRoutes(ResourceClass);
|
43 | }
|
44 | registerDeleteRoutes(ResourceClass) {
|
45 | let deleteMethods = ResourceClass.__delete;
|
46 | if (typeof deleteMethods !== 'undefined') {
|
47 | deleteMethods.forEach((deleteMethod) => {
|
48 | this.expressApp.delete(this.getPath(ResourceClass, deleteMethod), (req, res) => {
|
49 | let resource = new ResourceClass(...this.getDiArgs(ResourceClass));
|
50 | resource.req = req;
|
51 | resource.res = res;
|
52 | let paramList = this.getParamList(ResourceClass, deleteMethod.methodName, req);
|
53 | Promise.resolve(resource[deleteMethod.methodName].apply(resource, paramList)).then((returnValue) => {
|
54 | res.json(returnValue);
|
55 | }).catch((err) => res.status(err.statusCode || 500).json(err));
|
56 | });
|
57 | });
|
58 | }
|
59 | }
|
60 | registerPutRoutes(ResourceClass) {
|
61 | let putMethods = ResourceClass.__put;
|
62 | if (typeof putMethods !== 'undefined') {
|
63 | putMethods.forEach((putMethod) => {
|
64 | this.expressApp.put(this.getPath(ResourceClass, putMethod), (req, res) => {
|
65 | let resource = new ResourceClass(...this.getDiArgs(ResourceClass));
|
66 | resource.req = req;
|
67 | resource.res = res;
|
68 | let paramList = this.getParamList(ResourceClass, putMethod.methodName, req);
|
69 | Promise.resolve(resource[putMethod.methodName].apply(resource, paramList)).then((returnValue) => {
|
70 | res.json(returnValue);
|
71 | }).catch((err) => res.status(err.statusCode || 500).json(err));
|
72 | });
|
73 | });
|
74 | }
|
75 | }
|
76 | registerPostRoutes(ResourceClass) {
|
77 | let postMethods = ResourceClass.__post;
|
78 | if (typeof postMethods !== 'undefined') {
|
79 | postMethods.forEach((postMethod) => {
|
80 | this.expressApp.post(this.getPath(ResourceClass, postMethod), (req, res) => {
|
81 | let resource = new ResourceClass(...this.getDiArgs(ResourceClass));
|
82 | resource.req = req;
|
83 | resource.res = res;
|
84 | let paramList = this.getParamList(ResourceClass, postMethod.methodName, req);
|
85 | Promise.resolve(resource[postMethod.methodName].apply(resource, paramList)).then((returnValue) => {
|
86 | res.json(returnValue);
|
87 | }).catch((err) => res.status(err.statusCode || 500).json(err));
|
88 | });
|
89 | });
|
90 | }
|
91 | }
|
92 | registerGetRoutes(ResourceClass) {
|
93 | let getMethods = ResourceClass.__get;
|
94 | if (typeof getMethods !== 'undefined') {
|
95 | getMethods.forEach((getMethod) => {
|
96 | this.expressApp.get(this.getPath(ResourceClass, getMethod), (req, res) => {
|
97 | let resource = new ResourceClass(...this.getDiArgs(ResourceClass));
|
98 | resource.req = req;
|
99 | resource.res = res;
|
100 | let paramList = this.getParamList(ResourceClass, getMethod.methodName, req);
|
101 | Promise.resolve(resource[getMethod.methodName].apply(resource, paramList)).then((returnValue) => {
|
102 | res.json(returnValue);
|
103 | }).catch((err) => res.status(err.statusCode || 500).json(err));
|
104 | });
|
105 | });
|
106 | }
|
107 | }
|
108 | getDiArgs(injectable) {
|
109 | const paramTypes = Reflect.getMetadata('design:paramtypes', injectable);
|
110 | return lodash_1.map(paramTypes, (param) => ParkaApp.appInstance['__injector'].get(param));
|
111 | }
|
112 | getParamList(ResourceClass, methodName, req) {
|
113 | if (typeof ResourceClass.prototype[methodName].__params !== 'undefined') {
|
114 | return ResourceClass.prototype[methodName].__params.map((paramDef) => {
|
115 | if (paramDef.paramType === 'PATH_PARAM') {
|
116 | return req.params[paramDef.paramName];
|
117 | }
|
118 | else if (paramDef.paramType === 'QUERY_PARAM') {
|
119 | return req.query[paramDef.paramName];
|
120 | }
|
121 | else if (paramDef.paramType === 'REQUEST_BODY') {
|
122 | let constructedModel = this.constructCorrectType(req, paramDef);
|
123 | constructedModel.$validate();
|
124 | return constructedModel;
|
125 | }
|
126 | });
|
127 | }
|
128 | else {
|
129 | return [];
|
130 | }
|
131 | }
|
132 | constructCorrectType(req, paramDef) {
|
133 | if (typeof paramDef.constructor !== 'undefined') {
|
134 | if (paramDef.isArray === true) {
|
135 | return req.body.map((item) => {
|
136 | return paramDef.constructor.fromJson(item);
|
137 | });
|
138 | }
|
139 | else {
|
140 | return paramDef.constructor.fromJson(req.body);
|
141 | }
|
142 | }
|
143 | else {
|
144 | return req.body;
|
145 | }
|
146 | }
|
147 | getPath(ResourceClass, getMethod) {
|
148 | let path = [];
|
149 | if (this.config.routing && this.config.routing.prefix) {
|
150 | path.push(this.config.routing.prefix);
|
151 | }
|
152 | path.push(ResourceClass.__path);
|
153 | let methodPath = ResourceClass.prototype[getMethod.methodName].__path;
|
154 | if (typeof methodPath !== 'undefined') {
|
155 | path.push(methodPath);
|
156 | }
|
157 | return path.join('');
|
158 | }
|
159 | start() {
|
160 | let server = http.createServer(this.expressApp);
|
161 | server.listen(this.config.port, this.config.host, () => {
|
162 | console.log('server listening at ', server.address().address + ':' + server.address().port);
|
163 | });
|
164 | }
|
165 | parseAppConfig() {
|
166 | let configFileContents = YAML.load(this.configFile);
|
167 | this.config = new this.ConfigConstructor(configFileContents);
|
168 | return Promise.resolve(this.config.configureApplication)
|
169 | .then(() => {
|
170 | this['__providers'].push({
|
171 | provide: this.ConfigConstructor,
|
172 | useValue: this.config
|
173 | });
|
174 | });
|
175 | }
|
176 | configureExpressServer() {
|
177 | let app = express();
|
178 | const winstonLogger = this.configureLogger();
|
179 | this['__providers'].push({ provide: logger_1.Logger, useValue: winstonLogger });
|
180 | app.use(logger('combined', { immediate: true, stream: { write: (message) => winstonLogger.info(message) } }));
|
181 | app.use(bodyParser.json());
|
182 | app.use(bodyParser.urlencoded({ extended: false }));
|
183 | app.use(cookieParser());
|
184 | app.get('/', (req, res) => {
|
185 | res.send('it worked');
|
186 | });
|
187 | this.expressApp = app;
|
188 | }
|
189 | configureLogger() {
|
190 | const logger = new winston.Logger({
|
191 | exitOnError: false
|
192 | });
|
193 | if (this.config.logger && this.config.logger.transports) {
|
194 | lodash_1.forEach(this.config.logger.transports, (options, transportName) => {
|
195 | logger.add(winston.transports[transportName], options);
|
196 | });
|
197 | }
|
198 | else {
|
199 | logger.add(winston.transports.Console, {
|
200 | level: 'debug',
|
201 | handleExceptions: true,
|
202 | json: false,
|
203 | colorize: true,
|
204 | timestamp: true
|
205 | });
|
206 | }
|
207 | return logger;
|
208 | }
|
209 | configureInjector() {
|
210 | this.__injector = injection_js_1.ReflectiveInjector.resolveAndCreate(this['__providers']);
|
211 | }
|
212 | configureGlobalErrorHandling() {
|
213 |
|
214 | this.expressApp.use('*', (req, res, next) => {
|
215 | res.status(404)
|
216 | .json({
|
217 | status: 404,
|
218 | message: 'Not Found',
|
219 | stacktrace: this.getStacktrace()
|
220 | });
|
221 | });
|
222 | this.expressApp.use((err, req, res, next) => {
|
223 | if (err) {
|
224 | console.error(err.stack);
|
225 | }
|
226 |
|
227 | if (typeof err.statusCode === 'number' && typeof err.data !== 'undefined') {
|
228 | res.status(err.statusCode).json(err);
|
229 | }
|
230 | else {
|
231 | res.status(500)
|
232 | .json({
|
233 | status: 500,
|
234 | message: err.message,
|
235 | stacktrace: this.getStacktrace(err)
|
236 | });
|
237 | }
|
238 | });
|
239 | }
|
240 | getStacktrace(err) {
|
241 | if (typeof err !== 'undefined') {
|
242 | return (this.config.includeStacktraceInResponse) ? err.stack : null;
|
243 | }
|
244 | else {
|
245 | return (this.config.includeStacktraceInResponse) ? new Error().stack : null;
|
246 | }
|
247 | }
|
248 | configureDatabaseConneciton() {
|
249 | const conn = knex(this.config.db);
|
250 | objection_1.Model.knex(conn);
|
251 | }
|
252 | }
|
253 | exports.ParkaApp = ParkaApp;
|
254 |
|
\ | No newline at end of file |