UNPKG

8.12 kBJavaScriptView Raw
1'use strict';
2
3const path = require('path');
4const appRoot = require('app-root-path');
5const Kellner = require('kellner');
6
7const cqrsSwissknifeDomain = require('cqrs-swissknife/domain');
8const cqrsSwissknifeDenormalizerLoader = require('cqrs-swissknife/denormalizer/loader');
9const cqrsSwissknifeSagaLoader = require('cqrs-swissknife/saga/loader');
10
11const HooksManager = require('./HooksManager');
12
13const serviceLoader = require('./plugins/shared/ServicesStore/loader');
14const ServicesStore = require('./plugins/shared/ServicesStore');
15const ValidationService = require('./ValidationService');
16
17const queryParser = require('./queryParser');
18
19const debug = require('./debug');
20const tools = require('./tools');
21
22const { doForEachDir } = require('./utils/fs');
23const processEnv = require('./utils/processEnv');
24
25const restAndSpaLoader = require('./plugins/Gateways/shared/builder/loader');
26
27const crudLoader = require('./plugins/Crud/loader');
28
29const errors = require('./tools/errors');
30
31const loadConfig = require('./loadConfig');
32
33const OBLAK_ENV_NAME = 'OBLAK_ENV';
34
35class Oblak {
36 constructor(config = {}) {
37 // enviroment
38 this.env = config.env || processEnv(OBLAK_ENV_NAME) || 'debug';
39
40 this.hooksManager = new HooksManager();
41 this.hooksManager.registerHook('beforeLoad');
42 this.hooksManager.registerHook('beforeModuleLoad');
43 this.hooksManager.registerHook('afterModuleLoad');
44 this.hooksManager.registerHook('afterLoad');
45
46 this.hooksManager.registerHook('beforeInit');
47 this.hooksManager.registerHook('afterInit');
48
49 // config
50 this.rootPath = config.rootPath || appRoot.path;
51 const { name, version } = require(path.join(this.rootPath, 'package.json')); // eslint-disable-line
52
53 this.config = loadConfig.load(this.env, path.join(this.rootPath, 'config'), {}, config);
54
55 this.name = name;
56 this.version = version;
57
58 this.paths = {
59 domain: path.join(this.rootPath, 'app', 'domain'),
60 readmodels: path.join(this.rootPath, 'app', 'readmodels'),
61 rest: path.join(this.rootPath, 'app', 'gateways', 'rest'),
62 spa: path.join(this.rootPath, 'app', 'gateways', 'spa'),
63 crud: path.join(this.rootPath, 'app', 'crud'),
64 sagas: path.join(this.rootPath, 'app', 'sagas'),
65 services: path.join(this.rootPath, 'app', 'services'),
66 validation: path.join(this.rootPath, 'app', 'validation'),
67 };
68
69 // extend config with local config
70 this.config = loadConfig.merge(this.paths.rest, 'rest', this.config, true);
71 this.config = loadConfig.merge(this.paths.spa, 'spa', this.config, true);
72 this.config = loadConfig.merge(this.paths.spa, 'sagas', this.config, true);
73
74 this.queryParser = queryParser;
75
76 this._kellner = new Kellner({
77 name: this.name,
78 version: this.version,
79 config: this.config,
80 });
81
82 this._kellner.connections.use(Kellner.protocols.Amqp);
83 this._kellner.connections.use(Kellner.protocols.Mongodb);
84 this._kellner.connections.use(Kellner.protocols.Mongoose);
85 this._kellner.connections.use(Kellner.protocols.Elasticsearch);
86
87 this.errors = errors;
88
89 this.pluginsData = {};
90
91 this.processes = [];
92 this._processes = new Map();
93 this._intilizedProcesses = new Map();
94 }
95
96 _load() {
97 this.hooksManager.triggerHook(this, 'beforeLoad');
98
99 this._loadModule('domain', () => cqrsSwissknifeDomain.loader(this.paths.domain));
100 this._loadModule('readmodels', () => doForEachDir(this.paths.readmodels, (rmp, type) => {
101 if (!this.config.denormalizers[type])
102 return null;
103 return cqrsSwissknifeDenormalizerLoader(rmp);
104 }));
105 this._loadModule('sagas', () => doForEachDir(this.paths.sagas, (rmp, type) => {
106 const sagasConfig = this.config.sagas[type];
107 if (!sagasConfig)
108 return null;
109
110 return {
111 sagas: cqrsSwissknifeSagaLoader(rmp),
112 };
113 }));
114 this._loadModule('crud', () => crudLoader(this.paths.crud, this.config));
115 this._loadModule('rest', () => restAndSpaLoader(this.paths.rest, this.config.rest));
116 this._loadModule('spa', () => restAndSpaLoader(this.paths.spa, this.config.spa));
117
118 this.services = serviceLoader(this.paths.services);
119 // services should be accessasable system wide
120 this.servicesStore = new ServicesStore(this._kellner);
121
122
123 this.validation = ValidationService.loader(this.paths.validation);
124 this.validationService = new ValidationService(this);
125
126 this.hooksManager.triggerHook(this, 'afterLoad');
127 }
128
129 _linkProcesses(processes = this.processes) {
130 if (!processes)
131 return;
132
133 if (!Array.isArray(processes))
134 processes = [processes];
135
136 processes.forEach((process) => {
137 if (typeof process.processes === 'function')
138 this._linkProcesses(process.processes(this));
139
140 if (Array.isArray(process.processes))
141 this._linkProcesses(process.processes(this));
142
143 if (typeof process.baseLink === 'function')
144 process.baseLink(this._kellner, this);
145
146 this._processes.set(process.name, process);
147 });
148 }
149
150 async _initServices() {
151 await this.servicesStore.init(this);
152 }
153
154 _loadModule(name, loader) {
155 this.hooksManager.triggerHook(this, 'beforeModuleLoad', { name });
156 this[name] = loader();
157 this.hooksManager.triggerHook(this, 'afterModuleLoad', { name });
158 }
159
160 async _start(servicesToRun) {
161 this._processes.clear();
162
163 this.hooksManager.triggerHook(this, 'beforeInit', this);
164
165 await servicesToRun.reduce(async (promise, service) => {
166 await promise;
167 let customizedOblak = this;
168
169 if (typeof service.link === 'function')
170 customizedOblak = service.link(this._kellner, this) || this;
171
172 if (typeof service.init === 'function')
173 await service.init(customizedOblak);
174 }, Promise.resolve());
175
176 this.hooksManager.triggerHook(this, 'afterInit', this);
177
178 await this._kellner.init();
179 }
180
181 async _clear(servicesToClean) {
182 this._processes.clear();
183
184 await servicesToClean.reduce(async (promise, service) => {
185 await promise;
186 let customizedOblak = this;
187
188 if (typeof service.link === 'function')
189 customizedOblak = service.link(this._kellner, this) || this;
190
191 if (typeof service.clear === 'function')
192 await service.clear(customizedOblak);
193 }, Promise.resolve());
194
195 // await this._kellner.init();
196 }
197
198 async _exec(servicesToClean, konzola, commandCli, params) {
199 this._processes.clear();
200
201 await servicesToClean.reduce(async (promise, service) => {
202 await promise;
203
204 let customizedOblak = this;
205
206 if (typeof service.link === 'function')
207 customizedOblak = service.link(this._kellner, this) || this;
208
209 await service[commandCli](customizedOblak, konzola, commandCli, params);
210 this._intilizedProcesses.set(service.name, service);
211 }, Promise.resolve());
212 // await this._kellner.init();
213 }
214
215 use(processes) {
216 this.processes.push(processes);
217 }
218
219 async run({ options, services }) {
220 this._kellner.options(options);
221 const servicesToRun = Array.from(this._processes.values());
222 if (services.all)
223 return this._start(servicesToRun);
224 return this._start(servicesToRun.filter(p => !p.shouldRun || p.shouldRun(services)));
225 }
226
227 async init() {
228 this._load();
229 this._linkProcesses();
230 this._initServices();
231 return this._start(Array.from(this._processes.values()));
232 }
233
234 async clear() {
235 this._load();
236 this._linkProcesses();
237 this._initServices();
238 return this._clear(Array.from(this._processes.values()));
239 }
240
241 async exec(konzola, command, params) {
242 this._load();
243 this._linkProcesses();
244 this._initServices();
245
246 const commandCli = `${command}Command`;
247
248 const services = Array.from(this._processes.values()).filter((service) => {
249 const [group, name] = service.name.split(/:/);
250 if (!params[group])
251 return false;
252
253 if (params[group] !== true && !params[group].some(s => s === name))
254 return false;
255
256 if (typeof service[commandCli] !== 'function')
257 return false;
258
259 return true;
260 });
261
262 return this._exec(services, konzola, commandCli, params);
263 }
264
265 async close() {
266 for (const service of this._intilizedProcesses.values())
267 if (typeof service.close === 'function')
268 await service.close();
269 await this.servicesStore.close();
270 }
271
272 addHook(name, handler) {
273 return this.hooksManager.addHook(name, handler);
274 }
275}
276
277// static stuff
278Oblak.tools = tools;
279Oblak.debug = () => debug(Oblak);
280
281module.exports = Oblak;