dt-common-device 2.0.7 → 3.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 (186) hide show
  1. package/README.md +321 -99
  2. package/dist/alerts/Alert.model.d.ts +28 -0
  3. package/dist/alerts/Alert.model.js +222 -0
  4. package/dist/alerts/Alert.repository.d.ts +106 -0
  5. package/dist/alerts/Alert.repository.js +374 -0
  6. package/dist/alerts/Alert.service.d.ts +137 -0
  7. package/dist/alerts/Alert.service.js +476 -0
  8. package/dist/alerts/AlertBuilder.d.ts +87 -0
  9. package/dist/alerts/AlertBuilder.example.d.ts +11 -0
  10. package/dist/alerts/AlertBuilder.example.js +117 -0
  11. package/dist/alerts/AlertBuilder.js +185 -0
  12. package/dist/alerts/AlertService.example.d.ts +55 -0
  13. package/dist/alerts/AlertService.example.js +148 -0
  14. package/dist/alerts/alert.types.d.ts +57 -0
  15. package/dist/alerts/alert.types.js +22 -0
  16. package/dist/alerts/index.d.ts +3 -0
  17. package/dist/alerts/index.js +19 -0
  18. package/dist/config/config.d.ts +4 -4
  19. package/dist/config/config.js +3 -3
  20. package/dist/config/config.types.d.ts +19 -0
  21. package/dist/config/config.types.js +2 -0
  22. package/dist/connection/Connection.repository.d.ts +8 -0
  23. package/dist/connection/Connection.repository.js +92 -0
  24. package/dist/connection/Connection.service.d.ts +8 -0
  25. package/dist/connection/Connection.service.js +32 -0
  26. package/dist/connection/IConnection.d.ts +26 -0
  27. package/dist/connection/IConnection.js +14 -0
  28. package/dist/connection/index.d.ts +2 -0
  29. package/dist/connection/index.js +18 -0
  30. package/dist/device/cloud/entities/CloudDevice.d.ts +2 -2
  31. package/dist/device/cloud/entities/CloudDeviceService.d.ts +1 -1
  32. package/dist/device/cloud/entities/DeviceFactory.d.ts +1 -1
  33. package/dist/device/cloud/entities/DeviceFactory.js +1 -1
  34. package/dist/device/cloud/interfaces/ICloudDeviceService.d.ts +1 -1
  35. package/dist/device/cloud/interfaces/IRawDevice.d.ts +1 -1
  36. package/dist/device/local/interfaces/index.d.ts +2 -3
  37. package/dist/device/local/interfaces/index.js +2 -3
  38. package/dist/device/local/repository/Device.repository.js +3 -3
  39. package/dist/device/local/repository/Hub.repository.js +4 -4
  40. package/dist/device/local/repository/Schedule.repository.js +2 -2
  41. package/dist/device/local/services/Device.service.d.ts +2 -2
  42. package/dist/device/local/services/Device.service.js +1 -1
  43. package/dist/device/local/services/index.d.ts +0 -4
  44. package/dist/device/local/services/index.js +0 -4
  45. package/dist/events/BaseEventHandler.d.ts +2 -2
  46. package/dist/events/BaseEventHandler.js +2 -2
  47. package/dist/events/BaseEventTransformer.d.ts +1 -1
  48. package/dist/events/BaseEventTransformer.js +1 -1
  49. package/dist/events/DeviceEventHandler.d.ts +1 -1
  50. package/dist/events/DeviceEventHandler.js +2 -2
  51. package/dist/events/EventHandler.js +1 -1
  52. package/dist/events/EventHandlerOrchestrator.js +1 -1
  53. package/dist/events/EventProcessingService.js +1 -1
  54. package/dist/events/InternalEventSubscription.js +1 -1
  55. package/dist/index.d.ts +7 -5
  56. package/dist/index.js +16 -13
  57. package/dist/issues/Issue.model.d.ts +28 -0
  58. package/dist/issues/Issue.model.js +260 -0
  59. package/dist/issues/Issue.repository.d.ts +113 -0
  60. package/dist/issues/Issue.repository.js +401 -0
  61. package/dist/issues/Issue.service.d.ts +168 -0
  62. package/dist/issues/Issue.service.js +642 -0
  63. package/dist/issues/IssueBuilder.d.ts +109 -0
  64. package/dist/issues/IssueBuilder.example.d.ts +16 -0
  65. package/dist/issues/IssueBuilder.example.js +196 -0
  66. package/dist/issues/IssueBuilder.js +237 -0
  67. package/dist/issues/IssueService.example.d.ts +68 -0
  68. package/dist/issues/IssueService.example.js +177 -0
  69. package/dist/issues/index.d.ts +2 -0
  70. package/dist/issues/index.js +18 -0
  71. package/dist/issues/issue.types.d.ts +90 -0
  72. package/dist/issues/issue.types.js +40 -0
  73. package/dist/property/IProperty.d.ts +29 -0
  74. package/dist/property/IProperty.js +2 -0
  75. package/dist/property/Property.repository.d.ts +8 -0
  76. package/dist/property/Property.repository.js +95 -0
  77. package/dist/property/Property.service.d.ts +8 -0
  78. package/dist/property/Property.service.js +36 -0
  79. package/dist/property/index.d.ts +2 -0
  80. package/dist/property/index.js +18 -0
  81. package/dist/queue/entities/HybridHttpQueue.d.ts +23 -0
  82. package/dist/queue/entities/HybridHttpQueue.js +189 -0
  83. package/dist/queue/entities/index.d.ts +1 -0
  84. package/dist/queue/entities/index.js +17 -0
  85. package/dist/queue/index.d.ts +5 -0
  86. package/dist/queue/index.js +22 -0
  87. package/dist/queue/interfaces/IHttpRequestJob.d.ts +9 -0
  88. package/dist/queue/interfaces/IHttpRequestJob.js +2 -0
  89. package/dist/queue/interfaces/IHybridHttpQueue.d.ts +16 -0
  90. package/dist/queue/interfaces/IHybridHttpQueue.js +2 -0
  91. package/dist/queue/interfaces/IJobResult.d.ts +6 -0
  92. package/dist/queue/interfaces/IJobResult.js +2 -0
  93. package/dist/queue/interfaces/IRateLimitConfig.d.ts +5 -0
  94. package/dist/queue/interfaces/IRateLimitConfig.js +2 -0
  95. package/dist/queue/interfaces/index.d.ts +4 -0
  96. package/dist/queue/interfaces/index.js +20 -0
  97. package/dist/queue/services/QueueService.d.ts +19 -0
  98. package/dist/queue/services/QueueService.js +73 -0
  99. package/dist/queue/services/index.d.ts +1 -0
  100. package/dist/queue/services/index.js +17 -0
  101. package/dist/queue/types/http.types.d.ts +21 -0
  102. package/dist/queue/types/http.types.js +2 -0
  103. package/dist/queue/types/index.d.ts +2 -0
  104. package/dist/queue/types/index.js +18 -0
  105. package/dist/queue/types/queue.types.d.ts +35 -0
  106. package/dist/queue/types/queue.types.js +2 -0
  107. package/dist/queue/utils/index.d.ts +3 -0
  108. package/dist/queue/utils/index.js +19 -0
  109. package/dist/queue/utils/jobUtils.d.ts +10 -0
  110. package/dist/queue/utils/jobUtils.js +64 -0
  111. package/dist/queue/utils/queueUtils.d.ts +5 -0
  112. package/dist/queue/utils/queueUtils.js +59 -0
  113. package/dist/queue/utils/rateLimit.utils.d.ts +6 -0
  114. package/dist/queue/utils/rateLimit.utils.js +44 -0
  115. package/package.json +2 -1
  116. package/src/{device/local/models → alerts}/Alert.model.ts +1 -1
  117. package/src/{device/local/repository → alerts}/Alert.repository.ts +2 -2
  118. package/src/{device/local/services → alerts}/Alert.service.ts +14 -7
  119. package/src/{device/local/entities → alerts}/AlertBuilder.example.ts +2 -2
  120. package/src/{device/local/entities → alerts}/AlertBuilder.ts +14 -8
  121. package/src/{device/local/services → alerts}/AlertService.example.ts +6 -5
  122. package/src/{types → alerts}/alert.types.ts +2 -2
  123. package/src/alerts/index.ts +3 -0
  124. package/src/config/config.ts +7 -7
  125. package/src/{types → config}/config.types.ts +1 -1
  126. package/src/{device/local/repository → connection}/Connection.repository.ts +2 -2
  127. package/src/{device/local/services → connection}/Connection.service.ts +2 -2
  128. package/src/connection/index.ts +3 -0
  129. package/src/device/cloud/entities/CloudDevice.ts +2 -2
  130. package/src/device/cloud/entities/CloudDeviceService.ts +1 -1
  131. package/src/device/cloud/entities/DeviceFactory.ts +2 -2
  132. package/src/device/cloud/interfaces/ICloudDeviceService.ts +1 -1
  133. package/src/device/cloud/interfaces/IRawDevice.ts +1 -1
  134. package/src/device/local/interfaces/index.ts +2 -3
  135. package/src/device/local/repository/Device.repository.ts +3 -3
  136. package/src/device/local/repository/Hub.repository.ts +4 -4
  137. package/src/device/local/repository/Schedule.repository.ts +2 -2
  138. package/src/device/local/services/Device.service.ts +1 -1
  139. package/src/device/local/services/index.ts +0 -4
  140. package/{TROUBLESHOOTING.md → src/docs/TROUBLESHOOTING.md} +2 -2
  141. package/src/events/BaseEventHandler.ts +3 -3
  142. package/src/events/BaseEventTransformer.ts +2 -2
  143. package/src/events/DeviceEventHandler.ts +3 -3
  144. package/src/events/EventHandler.ts +1 -1
  145. package/src/events/EventHandlerOrchestrator.ts +2 -2
  146. package/src/events/EventProcessingService.ts +2 -2
  147. package/src/events/InternalEventSubscription.ts +2 -2
  148. package/src/index.ts +19 -13
  149. package/src/{device/local/models → issues}/Issue.model.ts +1 -1
  150. package/src/{device/local/repository → issues}/Issue.repository.ts +2 -2
  151. package/src/{device/local/services → issues}/Issue.service.ts +4 -4
  152. package/src/{device/local/entities → issues}/IssueBuilder.example.ts +1 -1
  153. package/src/{device/local/entities → issues}/IssueBuilder.ts +1 -1
  154. package/src/{device/local/services → issues}/IssueService.example.ts +6 -5
  155. package/src/issues/index.ts +2 -0
  156. package/src/{device/local/repository → property}/Property.repository.ts +2 -2
  157. package/src/{device/local/services → property}/Property.service.ts +1 -1
  158. package/src/property/index.ts +2 -0
  159. package/src/queue/entities/HybridHttpQueue.ts +196 -0
  160. package/src/queue/entities/index.ts +1 -0
  161. package/src/queue/index.ts +6 -0
  162. package/src/queue/interfaces/IHttpRequestJob.ts +10 -0
  163. package/src/queue/interfaces/IHybridHttpQueue.ts +23 -0
  164. package/src/queue/interfaces/IJobResult.ts +6 -0
  165. package/src/queue/interfaces/IRateLimitConfig.ts +5 -0
  166. package/src/queue/interfaces/index.ts +4 -0
  167. package/src/queue/services/QueueService.ts +39 -0
  168. package/src/queue/services/index.ts +1 -0
  169. package/src/queue/types/http.types.ts +22 -0
  170. package/src/queue/types/index.ts +2 -0
  171. package/src/queue/types/queue.types.ts +22 -0
  172. package/src/queue/utils/index.ts +3 -0
  173. package/src/queue/utils/jobUtils.ts +80 -0
  174. package/src/queue/utils/queueUtils.ts +90 -0
  175. package/src/queue/utils/rateLimit.utils.ts +58 -0
  176. package/tsconfig.json +4 -0
  177. package/src/device/local/entities/README.md +0 -173
  178. package/src/device/local/entities/index.ts +0 -2
  179. package/src/types/index.ts +0 -3
  180. /package/src/{device/local/interfaces → connection}/IConnection.ts +0 -0
  181. /package/src/{device/local/models → docs}/Alert.model.md +0 -0
  182. /package/src/{device/local/models/README.md → docs/Alerts&IssuesModel.md} +0 -0
  183. /package/src/{device/local/models → docs}/Issue.model.md +0 -0
  184. /package/{SECURITY.md → src/docs/SECURITY.md} +0 -0
  185. /package/src/{types → issues}/issue.types.ts +0 -0
  186. /package/src/{device/local/interfaces → property}/IProperty.ts +0 -0
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // Main entry point for dt-common-device
2
2
 
3
- // Cloud exports
3
+ // DEVICE EXPORTS
4
4
  export {
5
5
  CloudDevice,
6
6
  CloudDeviceService,
@@ -9,26 +9,32 @@ export {
9
9
  export {
10
10
  LocalDeviceService,
11
11
  LocalHubService,
12
- LocalConnectionService,
13
- LocalPropertyService,
14
12
  LocalScheduleService,
15
13
  } from "./device/local/services";
14
+ export * from "./device/local/interfaces";
15
+ export * from "./device/cloud/interfaces";
16
16
 
17
- // Events exports
18
- export * from "./events";
19
- export * from "./events/interfaces";
17
+ // CONNECTION EXPORTS
18
+ export * from "./connection";
20
19
 
21
- export * from "./device/cloud/interfaces";
20
+ // PROPERTY EXPORTS
21
+ export * from "./property";
22
22
 
23
- // Local exports
24
- export * from "./device/local/interfaces";
23
+ // EVENTS EXPORTS
25
24
  export * from "./events";
26
25
  export * from "./events/interfaces";
27
- // Types exports
28
- export * from "./types";
29
26
 
30
- // Redis utils
27
+ // ALERTS EXPORTS
28
+ export * from "./alerts";
29
+
30
+ // ISSUES EXPORTS
31
+ export * from "./issues";
32
+
33
+ // REDIS EXPORTS
31
34
  export * from "./utils";
32
35
 
33
- //initialize export
36
+ // QUEUE EXPORTS
37
+ export * from "./queue";
38
+
39
+ // CONFIG EXPORTS
34
40
  export { initialize, getConfig, shutdown } from "./config/config";
@@ -9,7 +9,7 @@ import {
9
9
  CreateIssueData,
10
10
  UpdateIssueData,
11
11
  AddCommentData,
12
- } from "../../../types/issue.types";
12
+ } from "./issue.types";
13
13
 
14
14
  // Comment sub-schema
15
15
  const CommentSchema = new Schema<IssueComment>(
@@ -1,5 +1,5 @@
1
1
  import { Service } from "typedi";
2
- import { IssueModel, IIssueDocument } from "../models/Issue.model";
2
+ import { IssueModel, IIssueDocument } from "./Issue.model";
3
3
  import {
4
4
  CreateIssueData,
5
5
  UpdateIssueData,
@@ -7,7 +7,7 @@ import {
7
7
  IssuePriority,
8
8
  IssuesCategory,
9
9
  EntityType,
10
- } from "../../../types/issue.types";
10
+ } from "./issue.types";
11
11
 
12
12
  @Service()
13
13
  export class IssueRepository {
@@ -1,6 +1,6 @@
1
1
  import { Service } from "typedi";
2
- import { IssueRepository } from "../repository/Issue.repository";
3
- import { IssueModel, IIssueDocument } from "../models/Issue.model";
2
+ import { IssueRepository } from "./Issue.repository";
3
+ import { IssueModel, IIssueDocument } from "./Issue.model";
4
4
  import {
5
5
  CreateIssueData,
6
6
  UpdateIssueData,
@@ -9,8 +9,8 @@ import {
9
9
  IssuePriority,
10
10
  IssuesCategory,
11
11
  EntityType,
12
- } from "../../../types/issue.types";
13
- import { IssueBuilder } from "../entities/IssueBuilder";
12
+ } from "./issue.types";
13
+ import { IssueBuilder } from "./IssueBuilder";
14
14
 
15
15
  @Service()
16
16
  export class IssueService {
@@ -1,5 +1,5 @@
1
1
  import { IssueBuilder } from "./IssueBuilder";
2
- import { IssuesCategory, IssuePriority, EntityType } from "../../../types/issue.types";
2
+ import { IssuesCategory, IssuePriority, EntityType } from "./issue.types";
3
3
 
4
4
  /**
5
5
  * Example usage of IssueBuilder
@@ -3,7 +3,7 @@ import {
3
3
  IssuesCategory,
4
4
  IssuePriority,
5
5
  EntityType,
6
- } from "../../../types/issue.types";
6
+ } from "./issue.types";
7
7
 
8
8
  /**
9
9
  * IssueBuilder - A builder pattern implementation for constructing CreateIssueData objects
@@ -1,6 +1,6 @@
1
1
  import { IssueService } from "./Issue.service";
2
- import { IssueBuilder } from "../entities/IssueBuilder";
3
- import { IssuesCategory, IssuePriority, EntityType } from "../../../types/issue.types";
2
+ import { IssueBuilder } from "./IssueBuilder";
3
+ import { IssuesCategory, IssuePriority, EntityType } from "./issue.types";
4
4
 
5
5
  /**
6
6
  * Example usage of the updated IssueService with IssueBuilder integration
@@ -291,17 +291,18 @@ export class IssueServiceExample {
291
291
  category: IssuesCategory.OPERATIONS,
292
292
  propertyId: "prop123",
293
293
  title: "Legacy Issue",
294
- description: "This issue was created using the old CreateIssueData format",
294
+ description:
295
+ "This issue was created using the old CreateIssueData format",
295
296
  entityId: "device456",
296
297
  entityType: EntityType.DEVICE,
297
298
  priority: IssuePriority.MEDIUM,
298
299
  assignedTo: "tech-support",
299
300
  createdBy: "legacy-system",
300
- dueDate: new Date("2024-01-20")
301
+ dueDate: new Date("2024-01-20"),
301
302
  };
302
303
 
303
304
  // This still works with the updated createIssue method
304
305
  const issue = await this.issueService.createIssue(issueData);
305
306
  return issue;
306
307
  }
307
- }
308
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./Issue.service";
2
+ export * from "./issue.types";
@@ -1,6 +1,6 @@
1
- import { getPostgresClient } from "../../../db";
1
+ import { getPostgresClient } from "../db";
2
2
  import { Service } from "typedi";
3
- import { IProperty, IPropertySettings } from "../interfaces/IProperty";
3
+ import { IProperty, IPropertySettings } from "./IProperty";
4
4
 
5
5
  @Service()
6
6
  export class PropertyRepository {
@@ -1,4 +1,4 @@
1
- import { PropertyRepository } from "../repository/Property.repository";
1
+ import { PropertyRepository } from "./Property.repository";
2
2
 
3
3
  export class LocalPropertyService {
4
4
  private readonly propertyRepository: PropertyRepository;
@@ -0,0 +1,2 @@
1
+ export * from "./IProperty";
2
+ export * from "./Property.service";
@@ -0,0 +1,196 @@
1
+ import { HttpCallOption } from "../types/http.types";
2
+ import { getConfig } from "../../config/config";
3
+ import axios from "axios";
4
+ import { publishAudit } from "dt-audit-library";
5
+ import { Service } from "typedi";
6
+ import { RateLimitUtils } from "../utils/rateLimit.utils";
7
+ import { JobUtils } from "../utils/jobUtils";
8
+ import { QueueUtils } from "../utils/queueUtils";
9
+ import { IRateLimitConfig, IJobResult, IHttpRequestJob } from "../interfaces";
10
+
11
+ @Service()
12
+ export class HybridHttpQueue {
13
+ private readonly queues = new Map<string, any>();
14
+ private readonly workers = new Map<string, any>();
15
+ private readonly rateLimitConfigs: Map<string, IRateLimitConfig>;
16
+ private readonly jobResults = new Map<string, IJobResult>();
17
+
18
+ constructor() {
19
+ this.rateLimitConfigs = RateLimitUtils.initializeRateLimitConfigs();
20
+ }
21
+
22
+ private async handleRateLimit(job: any): Promise<void> {
23
+ const { connectionId, provider, url, method, options } = job.data;
24
+ const microservice = options.queueOptions?.microservice || "default";
25
+ const isMaxRetries = job.attemptsMade >= 2;
26
+
27
+ await publishAudit({
28
+ eventType: isMaxRetries
29
+ ? "http.request.failed"
30
+ : "http.request.rateLimitExceeded",
31
+ properties: {
32
+ connectionId,
33
+ provider,
34
+ endpoint: url,
35
+ method,
36
+ timestamp: Date.now(),
37
+ queueId: job.id,
38
+ reason: isMaxRetries ? "max_retries_exceeded" : "rate_limit_exceeded",
39
+ ...(!isMaxRetries && {
40
+ retryCount: job.attemptsMade + 1,
41
+ maxRetries: 3,
42
+ }),
43
+ },
44
+ });
45
+
46
+ if (isMaxRetries) {
47
+ await this.queues
48
+ .get(QueueUtils.getQueueKey(microservice, connectionId, provider))
49
+ ?.obliterate({ force: true });
50
+ // Don't throw error for max retries - just return
51
+ return;
52
+ }
53
+
54
+ throw new Error("Rate limit exceeded");
55
+ }
56
+
57
+ private async processHttpRequest(job: any): Promise<any> {
58
+ const { connectionId, provider, url, method, options } = job.data;
59
+
60
+ // Check rate limit
61
+ if (
62
+ !(await RateLimitUtils.checkRateLimit(
63
+ connectionId,
64
+ provider,
65
+ this.rateLimitConfigs
66
+ ))
67
+ ) {
68
+ await this.handleRateLimit(job);
69
+ return;
70
+ }
71
+
72
+ try {
73
+ getConfig().LOGGER.info(
74
+ `Executing HTTP request: ${method} ${url} for ${provider}`
75
+ );
76
+
77
+ const response = await axios({
78
+ method: method.toLowerCase(),
79
+ url: url,
80
+ headers: options.headers || {},
81
+ timeout: 30000,
82
+ ...(options.body && { data: options.body }),
83
+ ...(options.params && { params: options.params }),
84
+ });
85
+
86
+ getConfig().LOGGER.info(
87
+ `HTTP request successful: ${method} ${url} for ${provider}`
88
+ );
89
+ return response.data;
90
+ } catch (error: any) {
91
+ getConfig().LOGGER.error(
92
+ `HTTP request failed ${job.id}: ${error.message}`
93
+ );
94
+
95
+ await publishAudit({
96
+ eventType: "http.request.error",
97
+ properties: {
98
+ connectionId,
99
+ provider,
100
+ endpoint: url,
101
+ method,
102
+ timestamp: Date.now(),
103
+ queueId: job.id,
104
+ reason: "execution_error",
105
+ errorMessage: error.message,
106
+ },
107
+ });
108
+
109
+ throw new Error(`HTTP request failed: ${error.message}`);
110
+ }
111
+ }
112
+
113
+ async request(options: {
114
+ method: string;
115
+ url: string;
116
+ body?: any;
117
+ headers?: Record<string, string>;
118
+ queueOptions?: {
119
+ connectionId: string;
120
+ connectionProvider: string;
121
+ microservice: string;
122
+ };
123
+ }): Promise<any> {
124
+ const { method, url, body, headers, queueOptions } = options;
125
+ // Create HttpCallOption object
126
+ const httpCallOption: HttpCallOption = {
127
+ headers,
128
+ body,
129
+ queueOptions,
130
+ };
131
+ // Call handleRequest with the constructed parameters
132
+ return this.handleRequest(url, method, httpCallOption);
133
+ }
134
+
135
+ async handleRequest(
136
+ url: string,
137
+ method: string,
138
+ options: HttpCallOption
139
+ ): Promise<any> {
140
+ const { connectionId, provider, microservice } =
141
+ JobUtils.extractConnectionDetails(options);
142
+ const queueKey = QueueUtils.getQueueKey(
143
+ microservice,
144
+ connectionId,
145
+ provider
146
+ );
147
+
148
+ getConfig().LOGGER.info(
149
+ `Queueing: ${method} ${url} -> ${provider} [${connectionId}]`
150
+ );
151
+
152
+ QueueUtils.getOrCreateWorker(
153
+ queueKey,
154
+ this.workers,
155
+ this.processHttpRequest.bind(this),
156
+ this.jobResults
157
+ );
158
+ const queue = QueueUtils.getOrCreateQueue(queueKey, this.queues);
159
+
160
+ const job = await queue.add(
161
+ "http-request",
162
+ {
163
+ microservice,
164
+ connectionId,
165
+ provider,
166
+ url,
167
+ method,
168
+ options,
169
+ timestamp: Date.now(),
170
+ },
171
+ {
172
+ attempts: 3,
173
+ backoff: { type: "exponential", delay: 5000 },
174
+ removeOnComplete: { age: 300, count: 1 },
175
+ removeOnFail: { age: 300, count: 1 },
176
+ }
177
+ );
178
+
179
+ getConfig().LOGGER.info(`Job ${job.id} queued, waiting for completion...`);
180
+ return JobUtils.waitForJobCompletion(job, queueKey, this.jobResults);
181
+ }
182
+
183
+ async shutdown(): Promise<void> {
184
+ getConfig().LOGGER.info("Shutting down HTTP queues...");
185
+
186
+ await Promise.all([
187
+ ...Array.from(this.workers.values()).map((worker: any) => worker.close()),
188
+ ...Array.from(this.queues.values()).map((queue: any) => queue.close()),
189
+ ]);
190
+
191
+ this.workers.clear();
192
+ this.queues.clear();
193
+ this.jobResults.clear();
194
+ getConfig().LOGGER.info("HTTP queues shutdown complete");
195
+ }
196
+ }
@@ -0,0 +1 @@
1
+ export * from "./HybridHttpQueue";
@@ -0,0 +1,6 @@
1
+ // Main queue exports
2
+ export * from "./entities";
3
+ export * from "./interfaces";
4
+ export * from "./services";
5
+ export * from "./types";
6
+ export * from "./utils";
@@ -0,0 +1,10 @@
1
+ import { HttpCallOption } from "../types/http.types";
2
+
3
+ export interface IHttpRequestJob {
4
+ connectionId: string;
5
+ provider: string;
6
+ url: string;
7
+ method: string;
8
+ options: HttpCallOption;
9
+ timestamp: number;
10
+ }
@@ -0,0 +1,23 @@
1
+ import { HttpCallOption } from "../types/http.types";
2
+
3
+ export interface IHybridHttpQueue {
4
+ request(options: {
5
+ method: string;
6
+ url: string;
7
+ body?: any;
8
+ headers?: Record<string, string>;
9
+ queueOptions?: {
10
+ connectionId: string;
11
+ connectionProvider: string;
12
+ microservice: string;
13
+ };
14
+ }): Promise<any>;
15
+
16
+ handleRequest(
17
+ url: string,
18
+ method: string,
19
+ options: HttpCallOption
20
+ ): Promise<any>;
21
+
22
+ shutdown(): Promise<void>;
23
+ }
@@ -0,0 +1,6 @@
1
+ export interface IJobResult {
2
+ result?: any;
3
+ error?: string;
4
+ resolved: boolean;
5
+ timestamp: number;
6
+ }
@@ -0,0 +1,5 @@
1
+ export interface IRateLimitConfig {
2
+ maxRequests: number;
3
+ windowMs: number;
4
+ provider: string;
5
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./IHybridHttpQueue";
2
+ export * from "./IHttpRequestJob";
3
+ export * from "./IJobResult";
4
+ export * from "./IRateLimitConfig";
@@ -0,0 +1,39 @@
1
+ import { Service } from "typedi";
2
+ import { HybridHttpQueue } from "../entities/HybridHttpQueue";
3
+ import { IHybridHttpQueue } from "../interfaces";
4
+ import { HttpCallOption } from "../types/http.types";
5
+
6
+ @Service()
7
+ export class QueueService implements IHybridHttpQueue {
8
+ private readonly hybridQueue: HybridHttpQueue;
9
+
10
+ constructor() {
11
+ this.hybridQueue = new HybridHttpQueue();
12
+ }
13
+
14
+ async request(options: {
15
+ method: string;
16
+ url: string;
17
+ body?: any;
18
+ headers?: Record<string, string>;
19
+ queueOptions?: {
20
+ connectionId: string;
21
+ connectionProvider: string;
22
+ microservice: string;
23
+ };
24
+ }): Promise<any> {
25
+ return this.hybridQueue.request(options);
26
+ }
27
+
28
+ async handleRequest(
29
+ url: string,
30
+ method: string,
31
+ options: HttpCallOption
32
+ ): Promise<any> {
33
+ return this.hybridQueue.handleRequest(url, method, options);
34
+ }
35
+
36
+ async shutdown(): Promise<void> {
37
+ return this.hybridQueue.shutdown();
38
+ }
39
+ }
@@ -0,0 +1 @@
1
+ export * from "./QueueService";
@@ -0,0 +1,22 @@
1
+ export interface HttpCallOption {
2
+ headers?: Record<string, string>;
3
+ body?: any;
4
+ params?: Record<string, any>;
5
+ queueOptions?: {
6
+ connectionId: string;
7
+ connectionProvider: string;
8
+ microservice: string;
9
+ };
10
+ }
11
+
12
+ export interface HttpRequestOptions {
13
+ method: string;
14
+ url: string;
15
+ body?: any;
16
+ headers?: Record<string, string>;
17
+ queueOptions?: {
18
+ connectionId: string;
19
+ connectionProvider: string;
20
+ microservice: string;
21
+ };
22
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./http.types";
2
+ export * from "./queue.types";
@@ -0,0 +1,22 @@
1
+ export interface QueueConfig {
2
+ concurrency: number;
3
+ removeOnComplete: { age: number; count: number };
4
+ removeOnFail: { age: number; count: number };
5
+ lockDuration: number;
6
+ stalledInterval: number;
7
+ }
8
+
9
+ export interface JobOptions {
10
+ attempts: number;
11
+ backoff: { type: string; delay: number };
12
+ removeOnComplete: { age: number; count: number };
13
+ removeOnFail: { age: number; count: number };
14
+ }
15
+
16
+ export interface QueueMetrics {
17
+ totalJobs: number;
18
+ completedJobs: number;
19
+ failedJobs: number;
20
+ pendingJobs: number;
21
+ activeJobs: number;
22
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./rateLimit.utils";
2
+ export * from "./jobUtils";
3
+ export * from "./queueUtils";
@@ -0,0 +1,80 @@
1
+ import { Job } from "bullmq";
2
+ import { IJobResult } from "../interfaces";
3
+ import { getConfig } from "../../config/config";
4
+
5
+ export class JobUtils {
6
+ static async waitForJobCompletion(
7
+ job: Job,
8
+ queueKey: string,
9
+ jobResults: Map<string, IJobResult>
10
+ ): Promise<any> {
11
+ return new Promise((resolve, reject) => {
12
+ let timeoutId: NodeJS.Timeout;
13
+ let checkCount = 0;
14
+ const maxChecks = 600;
15
+
16
+ const checkJob = async () => {
17
+ if (++checkCount >= maxChecks) {
18
+ clearTimeout(timeoutId);
19
+ return reject(new Error("Job timeout: Request took too long"));
20
+ }
21
+
22
+ try {
23
+ const state = await job.getState();
24
+
25
+ if (state === "completed") {
26
+ clearTimeout(timeoutId);
27
+ const memoryResult = jobResults.get(job.id!);
28
+
29
+ if (memoryResult?.resolved) {
30
+ return memoryResult.result !== undefined
31
+ ? resolve(memoryResult.result)
32
+ : reject(new Error(memoryResult.error || "Unknown error"));
33
+ }
34
+
35
+ // Fallback to job result
36
+ resolve(await job.returnvalue);
37
+ } else if (state === "failed") {
38
+ clearTimeout(timeoutId);
39
+ const memoryResult = jobResults.get(job.id!);
40
+ const errorMsg =
41
+ memoryResult?.error ||
42
+ (await job.failedReason) ||
43
+ "Unknown error";
44
+ reject(new Error(`Job failed: ${errorMsg}`));
45
+ } else {
46
+ timeoutId = setTimeout(checkJob, checkCount < 10 ? 50 : 100);
47
+ }
48
+ } catch (error: any) {
49
+ clearTimeout(timeoutId);
50
+ reject(new Error(`Error checking job: ${error.message}`));
51
+ }
52
+ };
53
+
54
+ checkJob();
55
+
56
+ // Backup timeout
57
+ setTimeout(() => {
58
+ clearTimeout(timeoutId);
59
+ reject(new Error("Request timeout: Maximum wait time exceeded"));
60
+ }, 60000);
61
+ });
62
+ }
63
+
64
+ static extractConnectionDetails(options: any): {
65
+ connectionId: string;
66
+ provider: string;
67
+ microservice: string;
68
+ } {
69
+ const {
70
+ connectionId,
71
+ connectionProvider: provider,
72
+ microservice,
73
+ } = options?.queueOptions ?? {};
74
+
75
+ if (!connectionId) throw new Error("Connection ID not found in options");
76
+ if (!provider) throw new Error("Connection provider not found in options");
77
+ if (!microservice) throw new Error("Microservice not found in options");
78
+ return { connectionId, provider, microservice };
79
+ }
80
+ }