UNPKG

8.2 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 = this.config.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 tasks: path.join(this.rootPath, 'app', 'tasks'),
68 };
69
70 // extend config with local config
71 this.config = loadConfig.merge(this.paths.rest, 'rest', this.config, true);
72 this.config = loadConfig.merge(this.paths.spa, 'spa', this.config, true);
73 this.config = loadConfig.merge(this.paths.spa, 'sagas', this.config, true);
74
75 this.queryParser = queryParser;
76
77 this._kellner = new Kellner({
78 name: this.name,
79 version: this.version,
80 config: this.config,
81 });
82
83 this._kellner.connections.use(Kellner.protocols.Amqp);
84 this._kellner.connections.use(Kellner.protocols.Mongodb);
85 this._kellner.connections.use(Kellner.protocols.Mongoose);
86 this._kellner.connections.use(Kellner.protocols.Elasticsearch);
87
88 this.errors = errors;
89
90 this.pluginsData = {};
91
92 this.processes = [];
93 this._processes = new Map();
94 this._intilizedProcesses = new Map();
95 }
96
97 _load() {
98 this.hooksManager.triggerHook(this, 'beforeLoad');
99
100 this._loadModule('domain', () => cqrsSwissknifeDomain.loader(this.paths.domain));
101 this._loadModule('readmodels', () => doForEachDir(this.paths.readmodels, (rmp, type) => {
102 if (!this.config.denormalizers[type])
103 return null;
104 return cqrsSwissknifeDenormalizerLoader(rmp);
105 }));
106 this._loadModule('sagas', () => doForEachDir(this.paths.sagas, (rmp, type) => {
107 const sagasConfig = this.config.sagas[type];
108 if (!sagasConfig)
109 return null;
110
111 return {
112 sagas: cqrsSwissknifeSagaLoader(rmp),
113 };
114 }));
115 this._loadModule('crud', () => crudLoader(this.paths.crud, this.config));
116 this._loadModule('rest', () => restAndSpaLoader(this.paths.rest, this.config.rest));
117 this._loadModule('spa', () => restAndSpaLoader(this.paths.spa, this.config.spa));
118
119 this.services = serviceLoader(this.paths.services);
120 // services should be accessasable system wide
121 this.servicesStore = new ServicesStore(this._kellner);
122
123
124 this.validation = ValidationService.loader(this.paths.validation);
125 this.validationService = new ValidationService(this);
126
127 this.hooksManager.triggerHook(this, 'afterLoad');
128 }
129
130 _linkProcesses(processes = this.processes) {
131 if (!processes)
132 return;
133
134 if (!Array.isArray(processes))
135 processes = [processes];
136
137 processes.forEach((process) => {
138 if (typeof process.processes === 'function')
139 this._linkProcesses(process.processes(this));
140
141 if (Array.isArray(process.processes))
142 this._linkProcesses(process.processes(this));
143
144 if (typeof process.baseLink === 'function')
145 process.baseLink(this._kellner, this);
146
147 this._processes.set(process.name, process);
148 });
149 }
150
151 async _initServices() {
152 await this.servicesStore.init(this);
153 }
154
155 _loadModule(name, loader) {
156 this.hooksManager.triggerHook(this, 'beforeModuleLoad', { name });
157 this[name] = loader();
158 this.hooksManager.triggerHook(this, 'afterModuleLoad', { name });
159 }
160
161 async _start(servicesToRun) {
162 this._processes.clear();
163
164 this.hooksManager.triggerHook(this, 'beforeInit', this);
165
166 await servicesToRun.reduce(async (promise, service) => {
167 await promise;
168 let customizedOblak = this;
169
170 if (typeof service.link === 'function')
171 customizedOblak = service.link(this._kellner, this) || this;
172
173 if (typeof service.init === 'function')
174 await service.init(customizedOblak);
175 }, Promise.resolve());
176
177 this.hooksManager.triggerHook(this, 'afterInit', this);
178
179 await this._kellner.init();
180 }
181
182 async _clear(servicesToClean) {
183 this._processes.clear();
184
185 await servicesToClean.reduce(async (promise, service) => {
186 await promise;
187 let customizedOblak = this;
188
189 if (typeof service.link === 'function')
190 customizedOblak = service.link(this._kellner, this) || this;
191
192 if (typeof service.clear === 'function')
193 await service.clear(customizedOblak);
194 }, Promise.resolve());
195
196 // await this._kellner.init();
197 }
198
199 async _exec(servicesToClean, konzola, commandCli, params) {
200 this._processes.clear();
201
202 await servicesToClean.reduce(async (promise, service) => {
203 await promise;
204
205 let customizedOblak = this;
206
207 if (typeof service.link === 'function')
208 customizedOblak = service.link(this._kellner, this) || this;
209
210 await service[commandCli](customizedOblak, konzola, commandCli, params);
211 this._intilizedProcesses.set(service.name, service);
212 }, Promise.resolve());
213 // await this._kellner.init();
214 }
215
216 use(processes) {
217 this.processes.push(processes);
218 }
219
220 async run({ options, services }) {
221 this._kellner.options(options);
222 const servicesToRun = Array.from(this._processes.values());
223 if (services.all)
224 return this._start(servicesToRun);
225 return this._start(servicesToRun.filter(p => !p.shouldRun || p.shouldRun(services)));
226 }
227
228 async init() {
229 this._load();
230 this._linkProcesses();
231 this._initServices();
232 return this._start(Array.from(this._processes.values()));
233 }
234
235 async clear() {
236 this._load();
237 this._linkProcesses();
238 this._initServices();
239 return this._clear(Array.from(this._processes.values()));
240 }
241
242 async exec(konzola, command, params) {
243 this._load();
244 this._linkProcesses();
245 this._initServices();
246
247 const commandCli = `${command}Command`;
248
249 const services = Array.from(this._processes.values()).filter((service) => {
250 const [group, name] = service.name.split(/:/);
251
252 if (!params[group])
253 return false;
254
255 if (params[group] !== true && !params[group].some(s => s === name))
256 return false;
257
258 if (typeof service[commandCli] !== 'function')
259 return false;
260
261 return true;
262 });
263
264 return this._exec(services, konzola, commandCli, params);
265 }
266
267 async close() {
268 for (const service of this._intilizedProcesses.values())
269 if (typeof service.close === 'function')
270 await service.close();
271 await this.servicesStore.close();
272 }
273
274 addHook(name, handler) {
275 return this.hooksManager.addHook(name, handler);
276 }
277
278}
279
280// static stuff
281Oblak.tools = tools;
282Oblak.debug = () => debug(Oblak);
283
284module.exports = Oblak;