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 = 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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
278 | Oblak.tools = tools;
|
279 | Oblak.debug = () => debug(Oblak);
|
280 |
|
281 | module.exports = Oblak;
|