evaengine 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/package.json +1 -1
  2. package/src/commands/index.js +12 -0
  3. package/src/commands/interface.js +45 -0
  4. package/src/commands/make_entity.js +490 -0
  5. package/src/commands/tramp.js +34 -0
  6. package/src/config/index.js +96 -0
  7. package/src/di.js +84 -0
  8. package/src/engine.js +551 -0
  9. package/src/entities/index.js +273 -0
  10. package/src/exceptions/index.js +501 -0
  11. package/src/middlewares/auth.js +67 -0
  12. package/src/middlewares/auth_kong.js +28 -0
  13. package/src/middlewares/debug.js +88 -0
  14. package/src/middlewares/index.js +18 -0
  15. package/src/middlewares/providers.js +73 -0
  16. package/src/middlewares/session.js +58 -0
  17. package/src/middlewares/trace.js +229 -0
  18. package/src/middlewares/validator.js +50 -0
  19. package/src/middlewares/view_cache.js +126 -0
  20. package/src/services/cache.js +223 -0
  21. package/src/services/config.js +111 -0
  22. package/src/services/env.js +28 -0
  23. package/src/services/event_manager.js +84 -0
  24. package/src/services/http_client.js +160 -0
  25. package/src/services/index.js +27 -0
  26. package/src/services/interface.js +5 -0
  27. package/src/services/joi.js +13 -0
  28. package/src/services/jwt_token.js +89 -0
  29. package/src/services/jwt_token_kong.js +75 -0
  30. package/src/services/logger.js +148 -0
  31. package/src/services/namespace.js +176 -0
  32. package/src/services/now.js +49 -0
  33. package/src/services/providers.js +158 -0
  34. package/src/services/redis.js +55 -0
  35. package/src/services/rest_client.js +112 -0
  36. package/src/swagger/index.js +669 -0
  37. package/src/utils/api_scaffold.js +240 -0
  38. package/src/utils/case_converter.js +33 -0
  39. package/src/utils/crc32.js +44 -0
  40. package/src/utils/datetime.js +13 -0
  41. package/src/utils/host.js +39 -0
  42. package/src/utils/index.js +35 -0
  43. package/src/utils/pagination.js +260 -0
  44. package/src/utils/random.js +23 -0
  45. package/src/utils/smart_query.js +352 -0
  46. package/src/utils/test.js +48 -0
  47. package/src/utils/wrapper.js +8 -0
@@ -0,0 +1,273 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import assert from 'assert';
4
+ import cloneDeep from 'lodash/cloneDeep.js';
5
+ import Sequelize from 'sequelize';
6
+ import { createRequire } from 'module';
7
+ import DI from '../di.js';
8
+ import { getMicroTimestamp } from '../utils/index.js';
9
+ import { StandardException } from '../exceptions/index.js';
10
+
11
+ const require = createRequire(import.meta.url);
12
+
13
+ //From https://github.com/angelxmoreno/sequelize-isunique-validator
14
+ Sequelize.prototype.validateIsUnique = (col, msg) => {
15
+ const conditions = { where: {} };
16
+ const message = msg || `${col} must be unique`;
17
+ return function v(value, next) {
18
+ const self = this;
19
+ this.Model.describe().then((schema) => {
20
+ conditions.where[col] = value;
21
+ Object
22
+ .keys(schema)
23
+ .filter(field => schema[field].primaryKey)
24
+ .forEach((pk) => {
25
+ conditions.where[pk] = { [Sequelize.Op.ne]: self[pk] };
26
+ });
27
+ }).then(() =>
28
+ self.Model.count(conditions).then((found) => {
29
+ if (found !== 0) {
30
+ return next(message);
31
+ }
32
+ return next();
33
+ })).catch(next);
34
+ };
35
+ };
36
+
37
+
38
+ export default class Entities {
39
+ /**
40
+ * @param {string} entitiesPath
41
+ * @param {Sequelize|Function} sequelizeInstance
42
+ */
43
+ constructor(entitiesPath, sequelizeInstance = null) {
44
+ this.entitiesPath = entitiesPath;
45
+ this.sequelize = sequelizeInstance;
46
+ this.entities = {};
47
+ this.scanned = false;
48
+ }
49
+
50
+ /**
51
+ * Add tracer info to end of SQL query logging
52
+ * @param options
53
+ * @returns {*}
54
+ */
55
+ static addTracer(options = {}) {
56
+ const logger = DI.get('logger');
57
+ return Object.assign(options, {
58
+ benchmark: true,
59
+ logging: (...args) => {
60
+ const tracer = DI.get('namespace').get('tracer');
61
+ const [query, cost] = args;
62
+ let pushed = false;
63
+ if (tracer && cost > 0) {
64
+ tracer.queries.push({
65
+ query,
66
+ cost: cost * 1000,
67
+ finishedAt: getMicroTimestamp()
68
+ });
69
+ pushed = true;
70
+ }
71
+ logger.verbose(...args, !pushed ? '| Tracer' : '');
72
+ }
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Scan all entity schemas under a special path
78
+ * @param entitiesPath
79
+ * @param withAssociate
80
+ * @returns {Entities}
81
+ */
82
+ scan(entitiesPath, withAssociate = true) {
83
+ assert(this.sequelize && this.sequelize instanceof Sequelize, 'Scan entities require a sequelize instance');
84
+
85
+ fs
86
+ .readdirSync(entitiesPath)
87
+ .filter((file) => {
88
+ const fileArray = file.split('.');
89
+ return (file.indexOf('.') !== 0) &&
90
+ (['js', 'es6', 'cjs'].indexOf(fileArray.pop()) !== -1) && (fileArray[0] !== 'index');
91
+ })
92
+ .forEach((file) => {
93
+ const entityModule = require(path.join(entitiesPath, file));
94
+ const entityFactory = entityModule.default || entityModule;
95
+ const entity = typeof entityFactory === 'function' ?
96
+ entityFactory(this.sequelize, Sequelize) : entityFactory;
97
+ this.entities[entity.name] = entity;
98
+ });
99
+
100
+ if (!withAssociate) {
101
+ return this;
102
+ }
103
+
104
+ Object.values(this.entities).forEach((entity) => {
105
+ if ('associate' in entity) {
106
+ entity.associate(this.entities);
107
+ }
108
+ });
109
+ return this;
110
+ }
111
+
112
+ /**
113
+ * Init sequelize instance from DI config
114
+ * @param withAssociate
115
+ * @returns {*}
116
+ */
117
+ init(withAssociate = true) {
118
+ if (this.sequelize && this.scanned) {
119
+ return this.sequelize;
120
+ }
121
+
122
+ const logger = DI.get('logger').getInstance();
123
+ if (!this.sequelize) {
124
+ const config = DI.get('config').get();
125
+ const ns = DI.get('namespace');
126
+ if (ns.isEnabled()) {
127
+ //Inject sequelize inner namespace, refer: http://docs.sequelizejs.com/en/latest/docs/transactions/
128
+ const context = ns.use().getContext();
129
+ if (typeof Sequelize.useCLS === 'function') {
130
+ Sequelize.useCLS(context);
131
+ } else {
132
+ Sequelize.cls = context;
133
+ }
134
+ }
135
+
136
+ const dbConfig = cloneDeep(config.db);
137
+ if (process.env.SEQUELIZE_REPLICATION_CONFIG_KEY) {
138
+ dbConfig.replication = dbConfig[process.env.SEQUELIZE_REPLICATION_CONFIG_KEY];
139
+ }
140
+ this.sequelize = new Sequelize(
141
+ config.db.database, null, null,
142
+ Object.assign({}, config.sequelize, dbConfig, Entities.addTracer())
143
+ );
144
+ } else {
145
+ this.sequelize = typeof this.sequelize === 'function' ? this.sequelize() : this.sequelize;
146
+ }
147
+
148
+ this.scan(this.entitiesPath, withAssociate);
149
+ this.scanned = true;
150
+
151
+ //TODO: mask password in logging
152
+ logger.debug('Entities init by scanned %s, Replication: %j', this.entitiesPath, this.sequelize.options.replication);
153
+ return this;
154
+ }
155
+
156
+ /**
157
+ * Shortcut for Sequelize query
158
+ * @param sql
159
+ * @param bind
160
+ * @param options
161
+ * @returns {*|{foo}|{}}
162
+ */
163
+ query(sql, bind = {}, options = {}) {
164
+ return this.getInstance().query(sql, Object.assign({
165
+ type: this.getSequelize().QueryTypes.SELECT
166
+ }, options, { bind }));
167
+ }
168
+
169
+ /**
170
+ * A shortcut to prevent repeat insert
171
+ * @param tableName
172
+ * @param input
173
+ * @param uniqueCondition
174
+ * @param transaction
175
+ * @param options
176
+ */
177
+ uniqueInsert({
178
+ tableName,
179
+ input,
180
+ uniqueCondition,
181
+ transaction
182
+ }, options = {}) {
183
+ const inputObj = Object.assign({}, input);
184
+ const typeAllowed = ['number', 'string', 'boolean'];
185
+ Object.entries(inputObj).forEach((p) => {
186
+ const valType = typeof p[1];
187
+ //Allow null type here
188
+ if (typeAllowed.indexOf(valType) === -1 && p[1] !== null) {
189
+ throw new StandardException(`SQL inputObj ${p[0]}:${p[1]} with unsupported type ${valType}.`);
190
+ } else if (valType === 'boolean') {
191
+ if (p[1] === true) {
192
+ inputObj[p[0]] = 1;
193
+ } else {
194
+ inputObj[p[0]] = 0;
195
+ }
196
+ }
197
+ });
198
+ const columns = Object.keys(inputObj);
199
+ const columnString = ['`', columns.join('`, `'), '`'].join('');
200
+ const valueString = Object.entries(inputObj).map(([key]) => `$${key} \`${key}\``).join(' , ');
201
+ const uniqueString = typeof uniqueCondition === 'string' ? uniqueCondition :
202
+ [`SELECT * FROM ${tableName} WHERE `, this.getInstance().dialect.QueryGenerator.getWhereConditions(uniqueCondition)].join('');
203
+ /*
204
+ Original Example:
205
+ entities.getInstance().query(`INSERT INTO ${tableName}
206
+ (userId, status)
207
+ (
208
+ SELECT *
209
+ FROM (SELECT $userId userId, $status status) AS tmp
210
+ WHERE NOT EXISTS (
211
+ SELECT id FROM ${auditTable} WHERE userId = $userId AND status = 'pending'
212
+ ) LIMIT 1
213
+ )`, { bind, transaction, type: entities.getSequelize().QueryTypes.INSERT });
214
+ */
215
+
216
+ const sql = `INSERT INTO ${tableName}
217
+ (${columnString})
218
+ (
219
+ SELECT *
220
+ FROM (SELECT ${valueString}) AS tmp
221
+ WHERE NOT EXISTS (
222
+ ${uniqueString} FOR UPDATE
223
+ ) LIMIT 1
224
+ )`; //add FOR UPDATE to use eXclusive Lock
225
+ return this.getInstance().query(sql, Object.assign({
226
+ bind: inputObj,
227
+ transaction,
228
+ type: Sequelize.QueryTypes.INSERT
229
+ }, options));
230
+ }
231
+
232
+ /**
233
+ * A short cut to start a database transaction
234
+ * @param options
235
+ */
236
+ getTransaction(options = {}) {
237
+ return this.getInstance().transaction(Object.assign({
238
+ autocommit: true
239
+ }, options));
240
+ }
241
+
242
+ /**
243
+ * @returns {Sequelize}
244
+ */
245
+ getSequelize() {
246
+ return Sequelize;
247
+ }
248
+
249
+ /**
250
+ * @returns {Sequelize}
251
+ */
252
+ getInstance() {
253
+ this.init();
254
+ return this.sequelize;
255
+ }
256
+
257
+ /**
258
+ * @param name
259
+ * @returns {Sequelize}
260
+ */
261
+ get(name) {
262
+ this.init();
263
+ return this.entities[name];
264
+ }
265
+
266
+ /**
267
+ * @returns {Object}
268
+ */
269
+ getAll() {
270
+ this.init();
271
+ return this.entities;
272
+ }
273
+ }