@vida-global/core 1.4.5 → 2.0.0

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 (55) hide show
  1. package/AGENTS.md +13 -0
  2. package/README.md +1 -0
  3. package/config/newrelic-config.js +118 -0
  4. package/index.js +9 -5
  5. package/lib/activeRecord/baseRecord.js +49 -3
  6. package/lib/apm/agent.js +22 -0
  7. package/lib/apm/index.js +54 -0
  8. package/lib/apm/utils.js +43 -0
  9. package/lib/http/client.js +16 -2
  10. package/lib/jobQueue/README.md +63 -0
  11. package/lib/jobQueue/abstractJob.js +103 -0
  12. package/lib/jobQueue/abstractJobComponent.js +37 -0
  13. package/lib/jobQueue/index.js +10 -0
  14. package/lib/jobQueue/jobImporter.js +19 -0
  15. package/lib/jobQueue/queue.js +161 -0
  16. package/lib/jobQueue/worker.js +273 -0
  17. package/lib/logger/README.md +33 -2
  18. package/lib/logger/index.js +117 -20
  19. package/lib/logger/serverMiddleware.js +41 -0
  20. package/lib/redis/redisClient.js +32 -24
  21. package/lib/server/controllerImporter.js +9 -47
  22. package/lib/server/server.js +34 -71
  23. package/lib/server/serverController.js +215 -23
  24. package/lib/utils/abstractAutoImporter.js +64 -0
  25. package/package.json +8 -6
  26. package/test/activeRecord/baseRecord.test.js +129 -0
  27. package/test/activeRecord/db/connection.test.js +4 -1
  28. package/test/activeRecord/db/connectionConfiguration.test.js +9 -3
  29. package/test/activeRecord/db/schema.test.js +217 -0
  30. package/test/activeRecord/helpers/baseRecordMocks.js +6 -4
  31. package/test/activeRecord/helpers/connection.js +0 -3
  32. package/test/activeRecord/helpers/connectionConfiguration.js +0 -8
  33. package/test/activeRecord/utils.test.js +72 -0
  34. package/test/apm/agent.test.js +56 -0
  35. package/test/apm/utils.test.js +121 -0
  36. package/test/helpers/env.js +33 -0
  37. package/test/http/client.test.js +3 -3
  38. package/test/jobQueue/abstractJob.test.js +307 -0
  39. package/test/jobQueue/abstractJobComponent.test.js +110 -0
  40. package/test/jobQueue/helpers/abstractJob.js +26 -0
  41. package/test/jobQueue/helpers/apmMock.js +20 -0
  42. package/test/jobQueue/helpers/bullmqMock.js +67 -0
  43. package/test/jobQueue/helpers/env.js +29 -0
  44. package/test/jobQueue/helpers/fixtureJobs/notAJob.js +8 -0
  45. package/test/jobQueue/helpers/fixtureJobs/testJobA.js +14 -0
  46. package/test/jobQueue/helpers/fixtureJobs/testJobB.js +14 -0
  47. package/test/jobQueue/helpers/loggerMock.js +43 -0
  48. package/test/jobQueue/helpers/worker.js +38 -0
  49. package/test/jobQueue/queue.test.js +320 -0
  50. package/test/jobQueue/worker.test.js +526 -0
  51. package/test/logger/index.test.js +61 -0
  52. package/test/logger/serverMiddleware.test.js +72 -0
  53. package/test/server/apiDocsGenerator.test.js +38 -0
  54. package/test/server/server.test.js +186 -4
  55. package/test/server/serverController.test.js +407 -5
package/AGENTS.md ADDED
@@ -0,0 +1,13 @@
1
+ # vida-core Guide
2
+
3
+ Use this guide to understand and write code for the vida-core repo.
4
+
5
+ # Style and conventions
6
+ @agents/style.md
7
+ @agents/server.md
8
+ @agents/db.md
9
+ @agents/apis.md
10
+ @agents/testing.md
11
+
12
+
13
+ # Helpers for common use cases
package/README.md CHANGED
@@ -7,3 +7,4 @@ This package is intended to contain core elements used across all Vida Apps. Kee
7
7
  - [HttpClient](lib/http)
8
8
  - [Logger](lib/logger)
9
9
  - [VidaServer](lib/server)
10
+ - [JobQueue](lib/jobQueue)
@@ -0,0 +1,118 @@
1
+ const path = require('path');
2
+
3
+
4
+ /**
5
+ * Shared New Relic agent configuration for Vida applications.
6
+ *
7
+ * Apps consume this via a thin `newrelic.js` shim at the application root:
8
+ *
9
+ * // <app>/newrelic.js
10
+ * module.exports = require('@vida-global/core/newrelic-config');
11
+ *
12
+ * The New Relic Node.js agent looks for `newrelic.js` in process.cwd() at
13
+ * startup, so each app needs the shim — but the canonical config lives here.
14
+ *
15
+ * Per-app overrides should be expressed via the standard NEW_RELIC_* env vars
16
+ * (e.g. NEW_RELIC_APP_NAME, NEW_RELIC_LICENSE_KEY, NEW_RELIC_LOG_LEVEL).
17
+ */
18
+
19
+ // App name + version are sourced from the consuming app's package.json.
20
+ // process.cwd() is the app root because that is where the agent loads
21
+ // newrelic.js from at startup.
22
+ const appPackage = require(path.join(process.cwd(), 'package.json'));
23
+ const isProd = process.env.NODE_ENV == 'production'
24
+ const envId = isProd ? '' : ` [${process.env.NODE_ENV || 'dev'}]`;
25
+ const appName = process.env.NEW_RELIC_APP_NAME || `${appPackage.name}${envId}`;
26
+
27
+
28
+ exports.config = {
29
+ app_name: [appName.toLowerCase()],
30
+ license_key: process.env.NEW_RELIC_LICENSE_KEY,
31
+
32
+ // NR has no dedicated "version" field; surface it as a label so it shows
33
+ // up as a filterable facet in the UI and on entity metadata.
34
+ labels: `version:${appPackage.version}`,
35
+
36
+ /**
37
+ * Partial approximation of New Relic's High-Security Mode. The HSM toggle
38
+ * is not available on our account, so each constraint is set explicitly
39
+ * here. Real HSM also enforces these at the collector; this configuration
40
+ * only enforces them at the agent, so it is critical that nothing in this
41
+ * block is loosened without a compliance review.
42
+ *
43
+ * Custom events (`recordCustomEvent`) and custom attributes
44
+ * (`addCustomAttribute`) are intentionally LEFT ENABLED — vida-core relies
45
+ * on them for job-queue and controller telemetry. Treat those channels as
46
+ * a trusted egress path: only attach identifier-grade values, never raw
47
+ * user input, request bodies, or PII.
48
+ */
49
+ ssl: true,
50
+ allow_all_headers: false,
51
+
52
+ transaction_tracer: {
53
+ enabled: true,
54
+ record_sql: 'obfuscated',
55
+ },
56
+
57
+ slow_sql: { enabled: false },
58
+
59
+ message_tracer: {
60
+ segment_parameters: { enabled: false },
61
+ },
62
+
63
+ attributes: {
64
+ exclude: [
65
+ // Header redactions (belt-and-suspenders with allow_all_headers: false)
66
+ 'request.headers.cookie',
67
+ 'request.headers.authorization',
68
+ 'request.headers.proxyAuthorization',
69
+ 'request.headers.setCookie*',
70
+ 'request.headers.x*',
71
+ 'response.headers.cookie',
72
+ 'response.headers.authorization',
73
+ 'response.headers.proxyAuthorization',
74
+ 'response.headers.setCookie*',
75
+ 'response.headers.x*',
76
+ // Query string and body parameters
77
+ 'request.parameters.*',
78
+ ],
79
+ },
80
+
81
+ // Cross-service trace propagation. Required for traces to stitch across
82
+ // any other instrumented Vida services this app calls.
83
+ distributed_tracing: { enabled: true },
84
+
85
+ // Do not forward app logs through the APM agent. Logging is handled by
86
+ // the platform's log pipeline.
87
+ application_logging: { enabled: false },
88
+
89
+ /**
90
+ * Ignore routine, non-business routes so they don't dominate transaction
91
+ * lists or skew apdex. Patterns are anchored regexes matched against the
92
+ * request URL path.
93
+ *
94
+ * / — vida-core SystemController hello world
95
+ * /system/healthCheck — vida-core SystemController health probe
96
+ * /hubspot/workflows/health — vida.live HubSpot integration health probe
97
+ * /pushWorker.js — vida.live static service-worker asset
98
+ */
99
+ rules: {
100
+ ignore: [
101
+ '^/system/healthCheck',
102
+ ],
103
+ },
104
+
105
+ /**
106
+ * The agent records errors via two independent paths:
107
+ * 1. exception capture (thrown Error or explicit noticeError call)
108
+ * 2. HTTP status-code capture (transaction ends with status >= 400)
109
+ *
110
+ * Without intervention, a thrown error that results in a 500 is reported
111
+ * twice. Ignoring the 400-599 range suppresses the status-code derived
112
+ * error events; the underlying exceptions are still captured via path #1.
113
+ */
114
+ error_collector: {
115
+ enabled: true,
116
+ ignore_status_codes: ['400-599'],
117
+ },
118
+ };
package/index.js CHANGED
@@ -1,8 +1,10 @@
1
- const httpLibs = require('./lib/http/client');
2
- const serverLibs = require('./lib/server');
3
- const { logger } = require('./lib/logger');
4
- const ActiveRecord = require('./lib/activeRecord');
5
- const redisLibs = require('./lib/redis');
1
+ const ActiveRecord = require('./lib/activeRecord');
2
+ const httpLibs = require('./lib/http/client');
3
+ const APM = require('./lib/apm');
4
+ const JobQueue = require('./lib/jobQueue');
5
+ const { logger } = require('./lib/logger');
6
+ const redisLibs = require('./lib/redis');
7
+ const serverLibs = require('./lib/server');
6
8
 
7
9
 
8
10
  const {
@@ -16,7 +18,9 @@ const serverErrorsToExport = Object.values(serverLibs).filter(obj => obj.prototy
16
18
 
17
19
  module.exports = {
18
20
  ActiveRecord,
21
+ APM,
19
22
  ...httpLibs,
23
+ JobQueue,
20
24
  logger,
21
25
  ...redisLibs,
22
26
  ...serverErrorsToExport,
@@ -1,7 +1,7 @@
1
1
  const { Connection, DEFAULT_DATABASE_ID } = require('./db/connection');
2
2
  const { getActiveRecordSchema } = require('./db/schema');
3
3
  const { logger } = require('../logger');
4
- const { Model, Op } = require('sequelize');
4
+ const { Model, Op, Sequelize } = require('sequelize');
5
5
  const nodeUtil = require('util');
6
6
  const { redisClientFactory } = require('../redis');
7
7
  const { camelize, tableize } = require('inflection');
@@ -19,7 +19,7 @@ class BaseRecord extends Model {
19
19
 
20
20
 
21
21
  constructor() {
22
- if (new.target == BaseRecord) throw new Error("BaseRecord must be subclasses");
22
+ if (new.target == BaseRecord) throw new Error("Cannot create an instance of BaseRecord");
23
23
  super(...arguments);
24
24
  }
25
25
 
@@ -77,6 +77,7 @@ class BaseRecord extends Model {
77
77
  }
78
78
 
79
79
  this.configureOverridenAccessors(schema);
80
+ this.configureVirtualAccessors(schema);
80
81
 
81
82
  return { schema, options };
82
83
  }
@@ -96,6 +97,40 @@ class BaseRecord extends Model {
96
97
  }
97
98
 
98
99
 
100
+ static configureVirtualAccessors(schema) {
101
+ const seen = new Set();
102
+ for (const proto of this._prototypeChain()) {
103
+ this._applyVirtualAccessorsFrom(proto, schema, seen);
104
+ }
105
+ }
106
+
107
+
108
+ static *_prototypeChain() {
109
+ let proto = this.prototype;
110
+ while (proto && proto !== Model.prototype && proto !== Object.prototype) {
111
+ yield proto;
112
+ proto = Object.getPrototypeOf(proto);
113
+ }
114
+ }
115
+
116
+
117
+ static _applyVirtualAccessorsFrom(proto, schema, seen) {
118
+ for (const name of Object.getOwnPropertyNames(proto)) {
119
+ const m = name.match(/_virtual(?<accessor>Get|Set)(?<propertyName>.*)/);
120
+ if (!m) continue;
121
+
122
+ const propertyName = camelize(m.groups.propertyName, true);
123
+ const accessor = m.groups.accessor.toLowerCase();
124
+ const key = `${propertyName}:${accessor}`;
125
+ if (seen.has(key)) continue;
126
+
127
+ schema[propertyName] = schema[propertyName] || {type: Sequelize.DataTypes.VIRTUAL};
128
+ schema[propertyName][accessor] = proto[name];
129
+ seen.add(key);
130
+ }
131
+ }
132
+
133
+
99
134
  static initializeHooks() {
100
135
  if (this.isCacheable) {
101
136
  this.addHook('afterSave', this._afterSaveCacheHook);
@@ -263,7 +298,9 @@ class BaseRecord extends Model {
263
298
 
264
299
 
265
300
  static async _getRedisClient() {
266
- return await redisClientFactory();
301
+ const client = redisClientFactory();
302
+ await client.connect();
303
+ return client;
267
304
  }
268
305
 
269
306
 
@@ -296,6 +333,15 @@ class BaseRecord extends Model {
296
333
  static debugLog(tag, log) {
297
334
  logger.debug(`[AR:${this.name}][${tag}] ${log}`);
298
335
  }
336
+
337
+
338
+ toApiResponse() {
339
+ const obj = this.dataValues;
340
+ for (const attr of Object.keys(obj)) {
341
+ obj[attr] = this[attr]; // Use overridden getters when available
342
+ }
343
+ return obj
344
+ }
299
345
  }
300
346
 
301
347
 
@@ -0,0 +1,22 @@
1
+ const ENABLED = Boolean(process.env.NEW_RELIC_LICENSE_KEY) && process.env.NODE_ENV !== 'test';
2
+
3
+
4
+ function buildStub() {
5
+ return {
6
+ addCustomAttribute() {},
7
+ instrumentLoadedModule() {},
8
+ noticeError() {},
9
+ recordCustomEvent() {},
10
+ recordMetric() {},
11
+ setControllerName() {},
12
+ setUserID() {},
13
+ startBackgroundTransaction(name, group, handler) { return handler(); },
14
+ startSegment(name, record, handler) { return handler(); },
15
+ };
16
+ }
17
+
18
+
19
+ module.exports = {
20
+ enabled: ENABLED,
21
+ client: ENABLED ? require('newrelic') : buildStub(),
22
+ };
@@ -0,0 +1,54 @@
1
+ const { logger } = require('../logger');
2
+ const { enabled, client } = require('./agent');
3
+ const utils = require('./utils');
4
+
5
+
6
+ let instrumented = false;
7
+ const toInstrument = [
8
+ 'aws-sdk',
9
+ 'bullmq',
10
+ 'express',
11
+ 'ioredis',
12
+ 'newrelic',
13
+ 'pg',
14
+ 'pg-pool',
15
+ 'redis',
16
+ ];
17
+
18
+
19
+ function instrumentAPM(packages={}) {
20
+ if (instrumented) return;
21
+ instrumented = true;
22
+
23
+ if (!enabled) return;
24
+
25
+ logger.addScope('apm');
26
+ for (const pkgName of toInstrument) {
27
+ instrumentPackageByName(pkgName)
28
+ }
29
+
30
+ for (const [pkgName, pkg] of Object.entries(packages)) {
31
+ client.instrumentLoadedModule(pkgName, pkg);
32
+ instrumentPackage(pkgName, pkg)
33
+ }
34
+ }
35
+
36
+
37
+ function instrumentPackageByName(pkgName) {
38
+ try {
39
+ const pkg = require(pkgName);
40
+ instrumentPackage(pkgName, pkg)
41
+ } catch(err) {}
42
+ }
43
+
44
+
45
+ function instrumentPackage(pkgName, pkg) {
46
+ client.instrumentLoadedModule(pkgName, pkg);
47
+ if (enabled) logger.apm.info(`Instrumenting ${pkgName}`);
48
+ }
49
+
50
+
51
+ module.exports = {
52
+ instrumentAPM,
53
+ ...utils
54
+ }
@@ -0,0 +1,43 @@
1
+ const { client } = require('./agent');
2
+
3
+
4
+ module.exports = {
5
+ addCustomAttribute(key, value) {
6
+ client.addCustomAttribute(key, value);
7
+ },
8
+
9
+
10
+ recordCustomEvent(eventType, attributes) {
11
+ client.recordCustomEvent(eventType, attributes);
12
+ },
13
+
14
+
15
+ recordMetric(name, value) {
16
+ client.recordMetric(name, value);
17
+ },
18
+
19
+
20
+ reportError(err, details={}) {
21
+ client.noticeError(err, details);
22
+ },
23
+
24
+
25
+ setControllerName(controllerName, action) {
26
+ client.setControllerName(controllerName, action);
27
+ },
28
+
29
+
30
+ setUserId(id) {
31
+ client.setUserID(id)
32
+ },
33
+
34
+
35
+ startBackgroundTransaction(name, group, handler) {
36
+ return client.startBackgroundTransaction(name, group, handler);
37
+ },
38
+
39
+
40
+ startSegment(name, record, handler) {
41
+ return client.startSegment(name, record, handler);
42
+ }
43
+ }
@@ -1,7 +1,11 @@
1
+ const APM = require('../apm');
1
2
  const { logger } = require('../logger');
2
3
  const { HttpError, HttpAbortError } = require('./error');
3
4
 
4
5
 
6
+ logger.addScope('http');
7
+
8
+
5
9
  class HttpClient {
6
10
  async get(endpoint, { requestParams, headers, timeout, signal }={}) {
7
11
  return await this.#makeRequest(endpoint, "GET", arguments[1]);
@@ -23,7 +27,12 @@ class HttpClient {
23
27
  }
24
28
 
25
29
 
26
- async #makeRequest(endpoint, method, { requestParams, body, headers, timeout, signal }={}) {
30
+ async #makeRequest(endpoint, method, opts={}) {
31
+ return await APM.startSegment(`HttpClient:${this.constructor.name}:${method}`, true, () => this.#_makeRequest(endpoint, method, opts));
32
+ }
33
+
34
+
35
+ async #_makeRequest(endpoint, method, { requestParams, body, headers, timeout, signal }={}) {
27
36
  this.#logRequest(method, endpoint);
28
37
 
29
38
  endpoint = `${this.urlRoot}${endpoint}`;
@@ -147,7 +156,7 @@ class HttpClient {
147
156
 
148
157
 
149
158
  #logRequest(method, endpoint) {
150
- logger.info(`API CALL: ${method} ${endpoint}`);
159
+ this.logger.debug(`API CALL: ${method} ${endpoint}`);
151
160
  }
152
161
 
153
162
 
@@ -162,6 +171,11 @@ class HttpClient {
162
171
  get urlRoot() {
163
172
  return "";
164
173
  }
174
+
175
+
176
+ get logger() {
177
+ return logger.http;
178
+ }
165
179
  }
166
180
 
167
181
 
@@ -0,0 +1,63 @@
1
+ # JobQueue
2
+ The `JobQueue` is a general purpose wrapper around an `BullMQ` job queue.
3
+
4
+
5
+ ## USAGE
6
+ The Node Job Queue has 3 key components: `Queues`, `Workers`, and `Jobs`.
7
+
8
+
9
+ ### QUEUES ###
10
+ A `Queue` object is used by a `Job` to add the `Job` and its associated arguments to the Redis queue for a `Worker` in a different process to dequeue and process. The work of a `Queue` is largely encapsulated by the `Job`, so you will rarely need to interact directly with a `Queue`. The exception is when ending a process. In order for your script to exit, you'll need to close any open `Queues`.
11
+
12
+ Closing a queue looks like this:
13
+ ```
14
+ const queue = getQueue(myQueueName);
15
+ await queue.close();
16
+ ```
17
+
18
+ Other less commonly needed `Queue` methods include:
19
+ ```
20
+ await queue.numQueuedJobs(); // returns the number of jobs in the queue
21
+ await queue.getQueuedJobs(); // returns an array of job data representing jobs in the queue
22
+ await queue.numActiveJobs(); // returns the number of jobs currently being worked on
23
+ await queue.getActiveJobs(); // returns an array of job data representing jobs currently being worked on
24
+ await queue.numFailedJobs(); // returns the failed jobs since the last failed jobs clear
25
+ await queue.getFailedJobs(); // returns an array of job data representing jobs failed since the last failed jobs clear
26
+ await queue.clearFailedJobs(); // removes all stored data about failed jobs. This should be done periodically to clear memory in Redis
27
+ await queue.clearCompletedJobs(); // removes all stored data about completed jobs. This should be done periodically to clear memory in Redis
28
+ await queue.clearQueuedJobs(); // removes all jobs from the queue
29
+ ```
30
+
31
+ ### WORKERS
32
+ A `Worker` is what runs on the server side, listening to Redis for new jobs in its queue. Starting a worker running on a server is simple.
33
+ ```
34
+ const worker = new Worker('myQueue');
35
+ await worker.listen();
36
+ ```
37
+
38
+ ### JOBS
39
+ A `Job` is the object that contains the code that will be run by the worker. All `Jobs` will subclass `AbstractJob` and implement the `run` and `queueName` methods. `Jobs` should be defined in the `jobs` directory at the top level of the repo. This will allow them to be auto registered by a worker.
40
+ ```
41
+ class MyJob extends AbstractJob {
42
+ async run(arg1, arg2...) {
43
+ // do work here
44
+ }
45
+
46
+ static queueName(arg1, arg2) {
47
+ return 'myQueue';
48
+ }
49
+ }
50
+ ```
51
+ Once you've defined your job's `run` and `queueName` methods, you can add it to the queue by running `queueJob`
52
+ ```
53
+ await MyJob.queueJob(arg1, arg2...)
54
+ ```
55
+
56
+ ## CONCURRENCY
57
+ While there should only be one `Worker` per process, each worker can process multiple jobs in parallel. Concurrency settings are defined through environment variables.
58
+
59
+ ### SIMPLE CONCURRENCY
60
+ In order to limit the number of jobs that a worker can run in parallel, simply set the `WORKER_CONCURRENCY` environment variable. If that variable is not set, concurrency will default to 1.
61
+
62
+ ### RATE LIMITING
63
+ To be added in a future release.
@@ -0,0 +1,103 @@
1
+ const APM = require('../apm');
2
+ const { getQueue } = require('./queue.js');
3
+ const { logger } = require('../logger');
4
+
5
+
6
+ const DEFAULT_NUM_RETRIES = 3;
7
+ const DEFAULT_RETRY_BACKOFF_TYPE = 'exponential';
8
+ const DEFAULT_RETRY_BACKOFF_DELAY = 2000;
9
+
10
+
11
+ class AbstractJob {
12
+ #id;
13
+ #updateProgress;
14
+
15
+
16
+ constructor() {
17
+ if (new.target === AbstractJob) throw new Error("Cannot create an instance of AbstractJob");
18
+ if (!this.constructor.queueName) throw new Error("No queue defined");
19
+ if (!this.run) throw new Error("No run method defined");
20
+ }
21
+
22
+
23
+ static queue(...jobArgs) {
24
+ return getQueue(this.queueName(...jobArgs));
25
+ }
26
+
27
+
28
+ static async queueJob(...jobArgs) {
29
+ const queue = this.queue(...jobArgs);
30
+ await queue.queueJob(this, jobArgs, this.queueSettings(...jobArgs));
31
+ }
32
+
33
+
34
+ async _run(id, args, updateProgress) {
35
+ this.#id = id;
36
+ this.#updateProgress = updateProgress;
37
+ await this.run(...args);
38
+ }
39
+
40
+
41
+ get id() {
42
+ return this.#id;
43
+ }
44
+
45
+
46
+ get logger() {
47
+ return logger[this.constructor.name].child(this.id);
48
+ }
49
+
50
+
51
+ async updateProgress() {
52
+ await this.#updateProgress(...arguments);
53
+ }
54
+
55
+
56
+ /***********************************************************************************************
57
+ * APM
58
+ ***********************************************************************************************/
59
+ addApmAttribute(key, value) {
60
+ APM.addCustomAttribute(key, value);
61
+ }
62
+
63
+
64
+ startApmSegment(name, record, handler) {
65
+ return APM.startSegment(name, record, handler);
66
+ }
67
+
68
+
69
+ /***********************************************************************************************
70
+ * SETTINGS
71
+ ***********************************************************************************************/
72
+ static queueSettings(...jobArgs) {
73
+ const settings = {
74
+ attempts: this.numRetries,
75
+ backoff: {
76
+ type: this.retryBackoffType,
77
+ delay: this.retryBackoffDelay
78
+ }
79
+ }
80
+
81
+ return settings;
82
+ }
83
+
84
+
85
+ static get numRetries() {
86
+ return DEFAULT_NUM_RETRIES;
87
+ }
88
+
89
+
90
+ static get retryBackoffType() {
91
+ return DEFAULT_RETRY_BACKOFF_TYPE;
92
+ }
93
+
94
+
95
+ static get retryBackoffDelay() {
96
+ return DEFAULT_RETRY_BACKOFF_DELAY;
97
+ }
98
+ }
99
+
100
+
101
+ module.exports = {
102
+ AbstractJob
103
+ }
@@ -0,0 +1,37 @@
1
+ class AbstractJobComponent {
2
+ get fullQueueName() {
3
+ return this.constructor.fullQueueName(this.queueName);
4
+ }
5
+
6
+
7
+ static fullQueueName(queueName) {
8
+ return `${queueName}-${this.env}`;
9
+ }
10
+
11
+
12
+ static get env() {
13
+ return process.env.NODE_ENV;
14
+ }
15
+
16
+
17
+ /***********************************************************************************************
18
+ * REDIS
19
+ ***********************************************************************************************/
20
+ get redisConnectionDetails() {
21
+ return {
22
+ host: this.host,
23
+ password: this.password,
24
+ port: this.port,
25
+ }
26
+ }
27
+
28
+ get host() { return process.env.REDIS_BULL_HOST }
29
+ get port() { return process.env.REDIS_BULL_PORT }
30
+ get password() { return process.env.REDIS_BULL_PASSWORD }
31
+
32
+ }
33
+
34
+
35
+ module.exports = {
36
+ AbstractJobComponent
37
+ }
@@ -0,0 +1,10 @@
1
+ const { AbstractJob } = require('./abstractJob');
2
+ const { getQueue } = require('./queue');
3
+ const { Worker } = require('./worker');
4
+
5
+
6
+ module.exports = {
7
+ AbstractJob,
8
+ getQueue,
9
+ Worker,
10
+ }
@@ -0,0 +1,19 @@
1
+ const { AbstractAutoImporter } = require('../utils/abstractAutoImporter');
2
+ const { AbstractJob } = require('./abstractJob');
3
+
4
+
5
+ class JobImporter extends AbstractAutoImporter {
6
+ get jobs() {
7
+ return this.imports;
8
+ }
9
+
10
+
11
+ shouldImport(obj) {
12
+ return obj.prototype instanceof AbstractJob;
13
+ }
14
+ }
15
+
16
+
17
+ module.exports = {
18
+ JobImporter
19
+ }