UNPKG

12.7 kBJavaScriptView Raw
1'use strict';
2
3var Path = require('path');
4var Util = require('util');
5var Express = require('express');
6var UUID = require('uuid');
7var Diread = require('diread');
8
9var toArray = require('./helper/toArray');
10var deepFreeze = require('./helper/deepFreeze');
11var pathWithoutExtension = require('./helper/pathWithoutExtension');
12var tryCatch = require('./helper/tryCatch');
13
14var debug = require('debug')('ifnode:application'); // eslint-disable-line
15var Log = require('./Log');
16
17var Extension = require('./application/Extension');
18var PLUGIN_TYPES = require('./PLUGIN_TYPES');
19var SchemaFactory = require('./SchemaFactory');
20var ConfigurationBuilder = require('./ConfigurationBuilder');
21
22var SchemasList = require('./application/SchemasList');
23var DAOList = require('./application/DAOList');
24var ModelBuilder = require('./application/ModelBuilder');
25
26var ComponentsBuilder = require('./application/ComponentsBuilder');
27var ControllersBuilder = require('./application/ControllersBuilder');
28
29var Controller = require('./Controller');
30var RestMiddleware = require('./middleware/rest');
31
32var IFNodeVirtualSchema = require('./../plugins/ifnode-virtual');
33var NodeHTTPServer = require('./../plugins/node-http_s-server');
34
35/**
36 * Creates a new Application instance
37 *
38 * @class Application
39 *
40 * @param {ApplicationOptions} options
41 */
42function Application(options) {
43 if(options.alias !== void 0 && typeof options.alias !== 'string') {
44 Log.error('application', 'Alias must be String');
45 }
46
47 this._require_cache = {};
48 this._extensions_cache = {};
49 this._project_folder = options.project_folder || options.projectFolder || Path.dirname(process.argv[1]);
50 this._backend_folder = Path.resolve(this._project_folder, 'protected/');
51
52 this._modules = [
53 IFNodeVirtualSchema
54 ];
55 this._models_builder = null;
56 this._controllers_builder = null;
57
58 this.id = UUID.v4();
59 this.alias = options.alias || this.id;
60 this.project_folder = this.projectFolder = this._project_folder;
61 this.backend_folder = this.backendFolder = this._backend_folder;
62
63 this.config = this._initialize_config(options.env || options.environment);
64 deepFreeze(this.config);
65
66 this.listener = this._initialize_listener();
67 this.connection = this._initialize_connection_server();
68
69 /**
70 * @deprecated Deprecated from 2.0.0 version. Connection server will be presented by app.connection
71 * @type {http.Server}
72 */
73 this.server = this.connection.server;
74
75 this.models = {};
76 this.components = {};
77 this._components_builder = new ComponentsBuilder(this.components, this.config.components);
78 this.controllers = {};
79}
80
81require('./application/middleware')(Application);
82
83/**
84 * Require module from start point like application project folder
85 *
86 * @param {string} id
87 * @returns {*}
88 */
89Application.prototype.require = function(id) {
90 if(!(id in this._require_cache)) {
91 this._require_cache[id] = require(Path.resolve(this.project_folder, id));
92 }
93
94 return this._require_cache[id];
95};
96
97/**
98 * Registered plugin(s) for application instance
99 *
100 * @param {string|Extension|Array.<string|Extension>} module
101 * @returns {Application}
102 */
103Application.prototype.register = function(module) {
104 var type_of = typeof module;
105
106 if(!(
107 type_of === 'string' ||
108 Array.isArray(module) ||
109 (type_of !== 'undefined' && type_of !== 'number')
110 )) {
111 Log.error('plugins', 'Wrong plugin type');
112 }
113
114 var self = this;
115 var modules = this._modules;
116
117 toArray(module).forEach(function(module) {
118 modules.push(typeof module === 'string' ?
119 self._require_module(module) :
120 module
121 );
122 });
123
124 return this;
125};
126
127/**
128 * Loads all maintenance parts of application
129 *
130 * @returns {Application}
131 */
132Application.prototype.load = function() {
133 this.models = this._initialize_models();
134 Object.freeze(this.models);
135
136 this._initialize_components();
137 // Object.freeze(this.components);
138
139 this.controllers = this._initialize_controllers();
140 Object.freeze(this.controllers);
141
142 this._is_loaded = true;
143
144 return this;
145};
146
147/**
148 *
149 * @param {string} id
150 * @returns {*}
151 */
152Application.prototype.extension = function(id) {
153 var cache = this._extensions_cache;
154
155 if(!(id in cache)) {
156 var custom_folder = this.config.application.folders.extensions;
157 var custom_full_path = Path.resolve(this._project_folder, custom_folder);
158 var extension_loader = new Extension(custom_full_path);
159
160 cache[id] = extension_loader.require(id);
161 }
162
163 return cache[id];
164};
165
166/**
167 *
168 * @param {string} id
169 * @returns {*}
170 */
171Application.prototype.ext = Util.deprecate(
172 Application.prototype.extension,
173 'Deprecated from 2.0.0 version. Needs to use app.extension() method'
174);
175
176/**
177 *
178 * @param {string} id
179 * @returns {Object}
180 */
181Application.prototype.component = function(id) {
182 if(id in this.components) {
183 return this.components[id];
184 }
185
186 var full_path = Path.resolve(this.config.application.folders.components, id);
187 var components_configs = this.config.components;
188 var components_builder = this._components_builder;
189 var component = components_builder.read_and_build_component(full_path, {
190 name: id,
191 config: (components_configs && components_configs[id]) || {}
192 });
193
194 components_builder.compile(this, full_path);
195
196 if (!this.components[id]) {
197 components_builder.save_component(component, id);
198 components_builder.components_compiled[id] = true;
199 }
200
201 return component;
202};
203
204/**
205 *
206 * @param {Object} model_config
207 * @param {Object} [options]
208 * @returns {Function}
209 * @constructor
210 */
211Application.prototype.Model = function(model_config, options) {
212 return this._models_builder.make(model_config, options);
213};
214
215/**
216 *
217 * @param {Object} [custom_component_config]
218 * @returns {Component}
219 */
220Application.prototype.Component = function(custom_component_config) {
221 var builder = this._components_builder;
222
223 return builder.make(
224 builder.build_component_config(custom_component_config)
225 );
226};
227
228/**
229 *
230 * @param {Object} [controller_config]
231 * @returns {Controller}
232 */
233Application.prototype.Controller = function(controller_config) {
234 return this._controllers_builder.make(controller_config);
235};
236
237/**
238 * Start web-server
239 *
240 * @deprecated
241 * @param {function} callback
242 */
243Application.prototype.run = Util.deprecate(function(callback) {
244 if(!this._is_loaded) {
245 this.load();
246 }
247
248 var self = this;
249 var connection = this.connection;
250
251 if(typeof callback === 'function') {
252 connection.listen(function() {
253 callback.call(self, self.config);
254 });
255 } else {
256 connection.listen();
257 }
258}, 'Deprecated from 2.0.0 version. Server will started by app.connection.listen()');
259
260/**
261 * Stop web-server
262 *
263 * @deprecated
264 * @param {function} callback
265 */
266Application.prototype.down = Util.deprecate(function(callback) {
267 this.connection.close(callback);
268}, 'Deprecated from 2.0.0 version. Server will be stopped by app.connection.close()');
269
270/**
271 * Initialize application instance configuration
272 *
273 * @param {string} environment
274 * @private
275 */
276Application.prototype._initialize_config = function(environment) {
277 var config_path;
278
279 if(environment) {
280 config_path = Path.resolve(this._project_folder, 'config/', environment);
281 }
282
283 return ConfigurationBuilder({
284 environment: environment,
285 project_folder: this._project_folder,
286 backend_folder: this._backend_folder,
287 config_path: config_path
288 });
289};
290
291/**
292 * Initialize server listener
293 *
294 * @returns {Express}
295 * @private
296 */
297Application.prototype._initialize_listener = function() {
298 var app = Express();
299 var config = this.config;
300 var app_config = config.application;
301
302 var middleware_configs = app_config.middleware;
303
304 app.use(RestMiddleware.response());
305 if(middleware_configs) {
306 this._initialize_middleware(middleware_configs, app);
307 }
308 app.use(RestMiddleware.request());
309
310 var express_configs = app_config.express;
311
312 Object.keys(express_configs).forEach(function(express_option) {
313 app.set(express_option, express_configs[express_option]);
314 });
315
316 return app;
317};
318
319/**
320 * Initialize native node.js (http,https, etc) server
321 *
322 * @returns {IConnectionServer}
323 * @private
324 */
325Application.prototype._initialize_connection_server = function() {
326 var site_config = this.config.site;
327 var connection = site_config.connection;
328
329 switch(connection) {
330 case 'http':
331 case 'https':
332 return new NodeHTTPServer(this.listener, site_config);
333
334 default:
335 /**
336 *
337 * @class IConnectionServer
338 */
339 var ServerCreator = this._require_module(connection);
340
341 return new ServerCreator(this.listener, site_config);
342 }
343};
344
345/**
346 *
347 * @private
348 */
349Application.prototype._initialize_models = function _initialize_models() {
350 var db = this.config.db;
351 var modules = this._modules;
352 var schemas_list = new SchemasList;
353
354 for(var i = 0; i < modules.length; ++i) {
355 var module = modules[i][PLUGIN_TYPES.SCHEMA];
356
357 if(module) {
358 var schema = SchemaFactory();
359
360 module(this, schema);
361 schemas_list.attach_schema(schema);
362 }
363 }
364
365 var models_builder = this._models_builder = new ModelBuilder(
366 new DAOList(schemas_list, db)
367 );
368
369 Diread({
370 src: this.config.application.folders.models
371 }).each(function(model_file_path) {
372 require(model_file_path);
373 });
374
375 return models_builder.compile_models(this);
376};
377
378/**
379 *
380 * @returns {Object.<string, Component>}
381 * @private
382 */
383Application.prototype._initialize_components = function _initialize_components() {
384 var self = this;
385 var components_builder = this._components_builder;
386 var Component = this.Component.bind(this);
387 var modules = this._modules;
388
389 for(var i = 0; i < modules.length; ++i) {
390 var module = modules[i][PLUGIN_TYPES.COMPONENT];
391
392 if(module) {
393 var component = module(this, Component);
394
395 if(component) {
396 components_builder.build_component(component, {});
397 }
398
399 components_builder.compile(this);
400 }
401 }
402
403 Diread({
404 src: this.config.application.folders.components,
405 directories: true,
406 level: 1,
407 mask: function(path) {
408 return path === pathWithoutExtension(path) ||
409 path.indexOf('.js') !== -1;
410 }
411 }).each(function(component_path) {
412 var autoformed_config = components_builder.build_and_memorize_config(component_path);
413
414 try {
415 components_builder.read_and_build_component(
416 component_path,
417 components_builder.build_component_config()
418 );
419 components_builder.compile(self, component_path);
420 } catch(error) {
421 /**
422 * Errors inside component will not catch by this handle
423 */
424 if(error.message.indexOf(component_path) === -1) {
425 throw error;
426 } else {
427 Log.warning(
428 'components',
429 'Cannot load component [' + autoformed_config.name + '] by path [' + component_path + ']'
430 );
431 }
432 }
433 });
434};
435
436/**
437 *
438 * @private
439 */
440Application.prototype._initialize_controllers = function _initialize_controllers() {
441 var controllers_builder = this._controllers_builder = new ControllersBuilder();
442 var modules = this._modules;
443
444 for(var i = 0; i < modules.length; ++i) {
445 var module = modules[i][PLUGIN_TYPES.CONTROLLER];
446
447 if(module) {
448 module(this, Controller);
449 }
450 }
451
452 controllers_builder.read_and_initialize_controllers(
453 this.config.application.folders.controllers, this
454 );
455
456 return controllers_builder.compile(this.listener);
457};
458
459/**
460 * Require node_module or application extension
461 *
462 * @returns {*}
463 * @private
464 */
465Application.prototype._require_module = function(module_name) {
466 var Module;
467
468 Module = tryCatch(function() {
469 return require(module_name);
470 });
471
472 if(Module) {
473 return Module;
474 }
475
476 var self = this;
477
478 Module = tryCatch(function() {
479 return self.extension(module_name);
480 });
481
482 if(Module) {
483 return Module;
484 }
485
486 Log.error('application', 'Cannot find node module or extension [' + module_name + '].');
487};
488
489module.exports = Application;