1 | 'use strict';
|
2 |
|
3 | const path = require('path');
|
4 | const appRoot = require('app-root-path');
|
5 | const Kellner = require('kellner');
|
6 |
|
7 | const cqrsSwissknifeDomain = require('cqrs-swissknife/domain');
|
8 | const cqrsSwissknifeDenormalizerLoader = require('cqrs-swissknife/denormalizer/loader');
|
9 | const cqrsSwissknifeSagaLoader = require('cqrs-swissknife/saga/loader');
|
10 |
|
11 | const HooksManager = require('./HooksManager');
|
12 |
|
13 | const serviceLoader = require('./plugins/shared/ServicesStore/loader');
|
14 | const ServicesStore = require('./plugins/shared/ServicesStore');
|
15 | const ValidationService = require('./ValidationService');
|
16 |
|
17 | const queryParser = require('./queryParser');
|
18 |
|
19 | const debug = require('./debug');
|
20 | const tools = require('./tools');
|
21 |
|
22 | const { doForEachDir } = require('./utils/fs');
|
23 | const processEnv = require('./utils/processEnv');
|
24 |
|
25 | const restAndSpaLoader = require('./plugins/Gateways/shared/builder/loader');
|
26 |
|
27 | const crudLoader = require('./plugins/Crud/loader');
|
28 |
|
29 | const errors = require('./tools/errors');
|
30 |
|
31 | const loadConfig = require('./loadConfig');
|
32 |
|
33 | const OBLAK_ENV_NAME = 'OBLAK_ENV';
|
34 |
|
35 | class Oblak {
|
36 | constructor(config = {}) {
|
37 |
|
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 |
|
50 | this.rootPath = config.rootPath || appRoot.path;
|
51 | const { name, version } = require(path.join(this.rootPath, 'package.json'));
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
281 | Oblak.tools = tools;
|
282 | Oblak.debug = () => debug(Oblak);
|
283 |
|
284 | module.exports = Oblak;
|