UNPKG

10.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const expression_1 = require("@gabliam/expression");
4const d = require("debug");
5const _ = require("lodash");
6const common_1 = require("./common");
7const constants_1 = require("./constants");
8const container_1 = require("./container");
9const loaders_1 = require("./loaders");
10const metadatas_1 = require("./metadatas");
11const plugin_list_1 = require("./plugin-list");
12const reflection_1 = require("./reflection");
13const registry_1 = require("./registry");
14const value_extractor_1 = require("./value-extractor");
15const debug = d('Gabliam:core');
16const DEFAULT_CONFIG = {
17 config: process.env.GABLIAM_CONFIG_PATH,
18};
19/**
20 * Gabliam
21 */
22class Gabliam {
23 /**
24 * Constructor
25 * @param {GabliamConfig|string} options?
26 */
27 constructor(options) {
28 this.container = container_1.createContainer();
29 /**
30 * Registry
31 */
32 this.registry = new registry_1.Registry();
33 /**
34 * Module loader
35 */
36 this.loaderModule = new loaders_1.LoaderModule();
37 /**
38 * Config loader
39 */
40 this.loaderConfig = new loaders_1.LoaderConfig();
41 /**
42 * Plugin list
43 */
44 this.pluginList = new plugin_list_1.PluginList();
45 if (options === undefined) {
46 this.options = DEFAULT_CONFIG;
47 }
48 else {
49 if (_.isString(options)) {
50 this.options = Object.assign({}, DEFAULT_CONFIG, { config: options });
51 }
52 else {
53 this.options = Object.assign({}, DEFAULT_CONFIG, options);
54 }
55 }
56 /**
57 * @TODO move in building phase
58 */
59 this.container.bind(constants_1.CORE_CONFIG).toConstantValue(this.options);
60 }
61 /**
62 * Add a plugin
63 * @param {GabliamPluginConstructor} plugin
64 * @returns Gabliam
65 */
66 addPlugin(plugin) {
67 this.pluginList.add(plugin);
68 return this;
69 }
70 /**
71 * Add any plugins
72 * @param {GabliamPluginConstructor[]} ...plugins
73 * @returns Gabliam
74 */
75 addPlugins(...plugins) {
76 for (const plugin of plugins) {
77 this.addPlugin(plugin);
78 }
79 return this;
80 }
81 /**
82 * Build gabliam
83 * @returns Promise
84 */
85 async build() {
86 this.pluginList.sort();
87 /**
88 * Load config file
89 */
90 await this._initializeConfig();
91 /**
92 * Loading phase
93 */
94 this.registry.addRegistry(this.loaderModule.load(this.options.scanPath, this.pluginList.plugins));
95 /**
96 * Binding phase
97 */
98 await this._bind();
99 /**
100 * Config phase
101 */
102 await this._loadConfig();
103 for (const plugin of this.pluginList.pluginsWithBuild) {
104 await common_1.toPromise(plugin.build(this.container, this.registry));
105 }
106 return this;
107 }
108 /**
109 * Build and start gabliam application
110 * @returns Promise
111 */
112 async buildAndStart() {
113 await this.build();
114 await this.start();
115 return this;
116 }
117 /**
118 * Stop and destroy gabliam application
119 * @returns Promise
120 */
121 async stopAndDestroy() {
122 await this.stop();
123 await this.destroy();
124 return this;
125 }
126 /**
127 * Start gabliam application
128 *
129 * call all plugin.start
130 * @returns Promise
131 */
132 async start() {
133 for (const plugin of this.pluginList.pluginsWithStart) {
134 await common_1.toPromise(plugin.start(this.container, this.registry));
135 }
136 return this;
137 }
138 /**
139 * Stop gabliam application
140 * call all plugin.stop
141 *
142 * @returns Promise
143 */
144 async stop() {
145 for (const plugin of this.pluginList.pluginsWithStop) {
146 await common_1.toPromise(plugin.stop(this.container, this.registry));
147 }
148 return this;
149 }
150 /**
151 * Destroy gabliam application
152 * call all plugin.destroy
153 *
154 * @returns Promise
155 */
156 async destroy() {
157 for (const plugin of this.pluginList.pluginsWithDestroy) {
158 await common_1.toPromise(plugin.destroy(this.container, this.registry));
159 }
160 const values = this.registry.get(constants_1.TYPE.PreDestroy);
161 const instanceToDestroy = async (value) => {
162 if (value.options) {
163 const instance = this.container.get(value.id);
164 for (const preDestroy of value.options.preDestroys) {
165 await common_1.callInstance(instance, preDestroy);
166 }
167 }
168 };
169 const p = [];
170 for (const value of values) {
171 p.push(instanceToDestroy(value));
172 }
173 await Promise.all(p);
174 return this;
175 }
176 /**
177 * Load config file and bind result in APP_CONFIG
178 */
179 async _initializeConfig() {
180 this.config = await this.loaderConfig.load(this.options.config);
181 const config = this.config;
182 this.container.bind(constants_1.APP_CONFIG).toConstantValue(config);
183 this.container
184 .bind(constants_1.VALUE_EXTRACTOR)
185 .toConstantValue(value_extractor_1.configureValueExtractor(this.container));
186 this.container
187 .bind(expression_1.ExpressionParser)
188 .toConstantValue(new expression_1.ExpressionParser(config));
189 }
190 /**
191 * Binding phase
192 * Binding of config and service classes.
193 * call all plugin.bind
194 */
195 async _bind() {
196 debug('_bind');
197 this.registry.getAllAutoBind().forEach(({ id, target }) => this.container
198 .bind(id)
199 .to(target)
200 .inSingletonScope());
201 for (const plugin of this.pluginList.pluginsWithBind) {
202 await common_1.toPromise(plugin.bind(this.container, this.registry));
203 }
204 }
205 /**
206 * Config phase
207 */
208 async _loadConfig() {
209 debug('_loadConfig');
210 // Get all config classes in registry
211 let configsRegistry = this.registry.get(constants_1.TYPE.Config);
212 debug('configsRegistry', configsRegistry);
213 if (configsRegistry) {
214 configsRegistry = _.sortBy(configsRegistry, 'options.order');
215 for (const { id: configId } of configsRegistry) {
216 // Get config instance
217 const confInstance = this.container.get(configId);
218 const ctor = confInstance.constructor;
219 // get all bean metadata in config classes
220 const beanMetadatas = reflection_1.reflection.propMetadataOfDecorator(ctor, metadatas_1.Bean);
221 // get on missing bean metadata
222 const onMissingBeanMetadatas = reflection_1.reflection.propMetadataOfDecorator(ctor, metadatas_1.OnMissingBean);
223 const beforeCreateMetas = Object.keys(reflection_1.reflection.propMetadataOfDecorator(ctor, metadatas_1.BeforeCreate));
224 const initMetadas = Object.keys(reflection_1.reflection.propMetadataOfDecorator(ctor, metadatas_1.Init));
225 // call all beforeCreate method if exist
226 if (Array.isArray(beforeCreateMetas)) {
227 // No promise.all and await because order of beans are important
228 for (const metada of beforeCreateMetas) {
229 await common_1.callInstance(confInstance, metada);
230 }
231 }
232 // If config has bean metadata
233 if (Object.keys(beanMetadatas).length) {
234 // No promise.all and await because order of beans are important
235 for (const [key, beans] of Object.entries(beanMetadatas)) {
236 const onMissingBeans = onMissingBeanMetadatas[key] || [];
237 // by default all bean are missing
238 let allMissing = true;
239 // if there are onMissingBeans config, check if bean exist or no
240 for (const onMissingBean of onMissingBeans) {
241 try {
242 this.container.getAll(onMissingBean.id);
243 allMissing = false;
244 }
245 catch (_a) { }
246 }
247 // if all beans are missing, so we create all beans
248 if (allMissing) {
249 for (const { id } of beans) {
250 const bean = await common_1.callInstance(confInstance, key);
251 this.container.bind(id).toConstantValue(bean);
252 if (bean === undefined || bean.constructor === undefined) {
253 continue;
254 }
255 const preDestroys = Object.keys(reflection_1.reflection.propMetadataOfDecorator(bean.constructor, metadatas_1.PreDestroy));
256 // bean can return undefined or can be a constant value
257 if (preDestroys.length) {
258 this.registry.add(constants_1.TYPE.PreDestroy, {
259 id,
260 target: bean,
261 options: {
262 preDestroys,
263 },
264 });
265 }
266 }
267 }
268 }
269 }
270 if (Array.isArray(initMetadas)) {
271 // No promise.all and await because order of beans are important
272 for (const metada of initMetadas) {
273 await common_1.callInstance(confInstance, metada);
274 }
275 }
276 for (const plugin of this.pluginList.pluginWithConfig) {
277 await common_1.toPromise(plugin.config(this.container, this.registry, confInstance));
278 }
279 }
280 }
281 debug('_loadConfig end');
282 }
283}
284exports.Gabliam = Gabliam;