dt-common-device 3.0.9 → 3.0.11
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.
- package/dist/config/config.d.ts +1 -2
- package/dist/config/config.js +4 -5
- package/dist/device/cloud/interface.d.ts +101 -0
- package/dist/device/cloud/interface.js +3 -0
- package/dist/device/cloud/interfaces/IDeviceConnectionService.d.ts +7 -0
- package/dist/device/cloud/interfaces/IDeviceConnectionService.js +3 -0
- package/dist/device/cloud/interfaces/IDevicesService.d.ts +9 -0
- package/dist/device/cloud/services/Device.service.d.ts +39 -0
- package/dist/device/cloud/services/Device.service.js +9 -0
- package/dist/device/cloud/services/DeviceCloudService.d.ts +42 -0
- package/dist/device/cloud/services/DeviceCloudService.js +59 -0
- package/dist/device/cloud/services/DeviceHub.service.d.ts +3 -0
- package/dist/device/cloud/services/DeviceHub.service.js +6 -0
- package/dist/device/cloud/services/Hub.service.d.ts +25 -0
- package/dist/device/cloud/services/Hub.service.js +9 -0
- package/dist/device/cloud/services/SmartThingsDeviceService.d.ts +38 -0
- package/dist/device/cloud/services/SmartThingsDeviceService.js +52 -0
- package/dist/device/index.d.ts +4 -0
- package/dist/device/index.js +20 -0
- package/dist/device/local/events/EventHandler.js +6 -6
- package/dist/device/local/events/Events.d.ts +12 -33
- package/dist/device/local/events/Events.js +12 -33
- package/dist/device/local/interface.d.ts +0 -0
- package/dist/device/local/interface.js +1 -0
- package/dist/device/local/interfaces/IDevice.d.ts +1 -0
- package/dist/device/local/repository/Schedule.repository.d.ts +0 -1
- package/dist/device/local/repository/Schedule.repository.js +6 -6
- package/dist/device/local/services/DeviceHub.service.d.ts +11 -0
- package/dist/device/local/services/DeviceHub.service.js +40 -0
- package/dist/queue/entities/HybridHttpQueue.d.ts +4 -14
- package/dist/queue/entities/HybridHttpQueue.js +31 -119
- package/dist/queue/interfaces/IHybridHttpQueue.d.ts +2 -12
- package/dist/queue/interfaces/IJobResult.d.ts +1 -8
- package/dist/queue/interfaces/index.d.ts +0 -1
- package/dist/queue/interfaces/index.js +0 -1
- package/dist/queue/services/QueueService.d.ts +2 -12
- package/dist/queue/types/queue.types.d.ts +10 -29
- package/dist/queue/utils/jobUtils.d.ts +0 -3
- package/dist/queue/utils/jobUtils.js +0 -48
- package/dist/queue/utils/queueUtils.d.ts +7 -0
- package/dist/queue/utils/queueUtils.js +113 -4
- package/package.json +6 -1
- package/.eslintrc.js +0 -44
- package/dist/audit/AuditProperties.d.ts +0 -16
- package/dist/audit/AuditUtils.d.ts +0 -2
- package/dist/audit/AuditUtils.js +0 -36
- package/src/alerts/Alert.model.ts +0 -289
- package/src/alerts/Alert.repository.ts +0 -487
- package/src/alerts/Alert.service.ts +0 -711
- package/src/alerts/AlertBuilder.example.ts +0 -126
- package/src/alerts/AlertBuilder.ts +0 -208
- package/src/alerts/AlertService.example.ts +0 -232
- package/src/alerts/alert.types.ts +0 -64
- package/src/alerts/index.ts +0 -3
- package/src/audit/AuditProperties.ts +0 -16
- package/src/audit/AuditUtils.ts +0 -38
- package/src/config/config.ts +0 -202
- package/src/config/config.types.ts +0 -21
- package/src/connection/Connection.repository.ts +0 -52
- package/src/connection/Connection.service.ts +0 -39
- package/src/connection/IConnection.ts +0 -27
- package/src/connection/index.ts +0 -3
- package/src/constants/ConnectionProviders.ts +0 -11
- package/src/constants/Event.ts +0 -89
- package/src/constants/Service.ts +0 -17
- package/src/constants/index.ts +0 -3
- package/src/db/db.ts +0 -24
- package/src/db/index.ts +0 -2
- package/src/db/redis.ts +0 -20
- package/src/device/cloud/entities/CloudDevice.ts +0 -40
- package/src/device/cloud/entities/CloudDeviceService.ts +0 -8
- package/src/device/cloud/entities/DeviceFactory.ts +0 -27
- package/src/device/cloud/entities/index.ts +0 -3
- package/src/device/cloud/interfaces/ICloudDevice.ts +0 -14
- package/src/device/cloud/interfaces/ICloudDeviceService.ts +0 -6
- package/src/device/cloud/interfaces/IDeviceFactory.ts +0 -5
- package/src/device/cloud/interfaces/IRawDataTransformer.ts +0 -5
- package/src/device/cloud/interfaces/IRawDevice.ts +0 -19
- package/src/device/cloud/interfaces/index.ts +0 -5
- package/src/device/local/interfaces/IDevice.ts +0 -61
- package/src/device/local/interfaces/IDtDevice.ts +0 -16
- package/src/device/local/interfaces/ISchedule.ts +0 -40
- package/src/device/local/interfaces/index.ts +0 -3
- package/src/device/local/repository/Device.repository.ts +0 -368
- package/src/device/local/repository/Hub.repository.ts +0 -107
- package/src/device/local/repository/Schedule.repository.ts +0 -72
- package/src/device/local/services/Device.service.ts +0 -436
- package/src/device/local/services/Hub.service.ts +0 -57
- package/src/device/local/services/Schedule.service.ts +0 -26
- package/src/device/local/services/index.ts +0 -3
- package/src/docs/Alert.model.md +0 -319
- package/src/docs/Alerts&IssuesModel.md +0 -312
- package/src/docs/Issue.model.md +0 -386
- package/src/docs/SECURITY.md +0 -67
- package/src/docs/TROUBLESHOOTING.md +0 -184
- package/src/events/BaseEventHandler.ts +0 -145
- package/src/events/BaseEventTransformer.ts +0 -97
- package/src/events/DeviceEventHandler.ts +0 -213
- package/src/events/DeviceEventTransformerFactory.ts +0 -77
- package/src/events/EventHandler.ts +0 -124
- package/src/events/EventHandlerOrchestrator.ts +0 -119
- package/src/events/EventProcessingService.ts +0 -248
- package/src/events/InternalEventSubscription.ts +0 -194
- package/src/events/index.ts +0 -9
- package/src/events/interfaces/DeviceEvent.ts +0 -56
- package/src/events/interfaces/IEventHandler.ts +0 -28
- package/src/events/interfaces/IEventTransformer.ts +0 -8
- package/src/events/interfaces/IInternalEvent.ts +0 -33
- package/src/events/interfaces/index.ts +0 -4
- package/src/index.ts +0 -43
- package/src/issues/Issue.model.ts +0 -350
- package/src/issues/Issue.repository.ts +0 -517
- package/src/issues/Issue.service.ts +0 -932
- package/src/issues/IssueBuilder.example.ts +0 -210
- package/src/issues/IssueBuilder.ts +0 -263
- package/src/issues/IssueService.example.ts +0 -310
- package/src/issues/index.ts +0 -2
- package/src/issues/issue.types.ts +0 -98
- package/src/property/IProperty.ts +0 -30
- package/src/property/Property.repository.ts +0 -53
- package/src/property/Property.service.ts +0 -38
- package/src/property/index.ts +0 -2
- package/src/queue/entities/HybridHttpQueue.ts +0 -274
- package/src/queue/entities/index.ts +0 -1
- package/src/queue/index.ts +0 -6
- package/src/queue/interfaces/IHttpRequestJob.ts +0 -10
- package/src/queue/interfaces/IHybridHttpQueue.ts +0 -25
- package/src/queue/interfaces/IJobResult.ts +0 -15
- package/src/queue/interfaces/IRateLimitConfig.ts +0 -5
- package/src/queue/interfaces/index.ts +0 -4
- package/src/queue/services/QueueService.ts +0 -40
- package/src/queue/services/index.ts +0 -1
- package/src/queue/types/http.types.ts +0 -23
- package/src/queue/types/index.ts +0 -2
- package/src/queue/types/queue.types.ts +0 -21
- package/src/queue/utils/index.ts +0 -3
- package/src/queue/utils/jobUtils.ts +0 -79
- package/src/queue/utils/queueUtils.ts +0 -84
- package/src/queue/utils/rateLimit.utils.ts +0 -131
- package/src/utils/http.utils.ts +0 -143
- package/src/utils/index.ts +0 -2
- package/src/utils/redis.utils.ts +0 -74
- package/tsconfig.json +0 -20
- /package/dist/{audit/AuditProperties.js → device/cloud/interfaces/IDevicesService.js} +0 -0
|
@@ -1,274 +0,0 @@
|
|
|
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 {
|
|
10
|
-
IRateLimitConfig,
|
|
11
|
-
IJobResult,
|
|
12
|
-
IHttpRequestJob,
|
|
13
|
-
IQueueResponse,
|
|
14
|
-
} from "../interfaces";
|
|
15
|
-
import { Queue } from "bullmq";
|
|
16
|
-
|
|
17
|
-
@Service()
|
|
18
|
-
export class HybridHttpQueue {
|
|
19
|
-
private readonly queues = new Map<string, any>();
|
|
20
|
-
private readonly workers = new Map<string, any>();
|
|
21
|
-
private readonly rateLimitConfigs: Map<string, IRateLimitConfig>;
|
|
22
|
-
private readonly jobResults = new Map<string, IJobResult>();
|
|
23
|
-
|
|
24
|
-
constructor() {
|
|
25
|
-
this.rateLimitConfigs = RateLimitUtils.initializeRateLimitConfigs();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private async handleRateLimitAndQueue(
|
|
29
|
-
url: string,
|
|
30
|
-
method: string,
|
|
31
|
-
options: HttpCallOption
|
|
32
|
-
): Promise<IQueueResponse> {
|
|
33
|
-
const { connectionId, provider, microservice } =
|
|
34
|
-
JobUtils.extractConnectionDetails(options);
|
|
35
|
-
const key = `rate_limit:${provider}:${connectionId}`;
|
|
36
|
-
const config = this.rateLimitConfigs.get(provider);
|
|
37
|
-
const windowMs = config?.windowMs ?? 60000;
|
|
38
|
-
|
|
39
|
-
const timestamps = await RateLimitUtils.getRawRequestTimestamps(key);
|
|
40
|
-
const now = Date.now();
|
|
41
|
-
const windowStart = now - windowMs;
|
|
42
|
-
const recentRequests = timestamps.filter((t) => t > windowStart);
|
|
43
|
-
const nextAvailableTime =
|
|
44
|
-
recentRequests.length > 0 ? recentRequests[0] + windowMs : now + 1000;
|
|
45
|
-
const delay = Math.max(nextAvailableTime - now, 1000); // at least 1s delay
|
|
46
|
-
|
|
47
|
-
// Create job data
|
|
48
|
-
const jobData = {
|
|
49
|
-
microservice,
|
|
50
|
-
connectionId,
|
|
51
|
-
provider,
|
|
52
|
-
url,
|
|
53
|
-
method,
|
|
54
|
-
options,
|
|
55
|
-
timestamp: Date.now(),
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// Add job to queue with delay (background processing)
|
|
59
|
-
const queueKey = QueueUtils.getQueueKey(
|
|
60
|
-
microservice,
|
|
61
|
-
connectionId,
|
|
62
|
-
provider
|
|
63
|
-
);
|
|
64
|
-
const queue = QueueUtils.getOrCreateQueue(queueKey, this.queues);
|
|
65
|
-
|
|
66
|
-
QueueUtils.getOrCreateWorker(
|
|
67
|
-
queueKey,
|
|
68
|
-
this.workers,
|
|
69
|
-
this.processHttpRequest.bind(this),
|
|
70
|
-
this.jobResults
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const job = await queue.add("http-request", jobData, {
|
|
74
|
-
delay,
|
|
75
|
-
attempts: 1,
|
|
76
|
-
removeOnComplete: { age: 300, count: 1 },
|
|
77
|
-
removeOnFail: { age: 300, count: 1 },
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
await publishAudit({
|
|
81
|
-
eventType: "http.request.rateLimitQueued",
|
|
82
|
-
properties: {
|
|
83
|
-
resource: microservice,
|
|
84
|
-
connectionId,
|
|
85
|
-
provider,
|
|
86
|
-
endpoint: url,
|
|
87
|
-
method,
|
|
88
|
-
timestamp: Date.now(),
|
|
89
|
-
queueId: job.id,
|
|
90
|
-
reason: "rate_limit_exceeded_queued",
|
|
91
|
-
delay,
|
|
92
|
-
estimatedProcessingTime: now + delay,
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Return immediate response to controller
|
|
97
|
-
return {
|
|
98
|
-
success: true,
|
|
99
|
-
queued: true,
|
|
100
|
-
estimatedProcessingTime: now + delay,
|
|
101
|
-
jobId: job.id,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private async processHttpRequest(job: any): Promise<any> {
|
|
106
|
-
const { connectionId, provider, url, method, options } = job.data;
|
|
107
|
-
|
|
108
|
-
const allowed = await RateLimitUtils.isRateLimitAllowed(
|
|
109
|
-
connectionId,
|
|
110
|
-
provider,
|
|
111
|
-
this.rateLimitConfigs
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
if (!allowed) {
|
|
115
|
-
// This shouldn't happen since we check before queuing, but handle it gracefully
|
|
116
|
-
getConfig().LOGGER.warn(
|
|
117
|
-
`Job ${job.id} still rate limited after delay, skipping`
|
|
118
|
-
);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
await RateLimitUtils.recordRequest(connectionId, provider);
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
getConfig().LOGGER.info(
|
|
126
|
-
`Executing HTTP request: ${method} ${url} for ${provider}`
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const response = await axios({
|
|
130
|
-
method: method.toLowerCase(),
|
|
131
|
-
url: url,
|
|
132
|
-
headers: options.headers || {},
|
|
133
|
-
timeout: 30000,
|
|
134
|
-
...(options.body && { data: options.body }),
|
|
135
|
-
...(options.params && { params: options.params }),
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
getConfig().LOGGER.info(
|
|
139
|
-
`HTTP request successful: ${method} ${url} for ${provider}`
|
|
140
|
-
);
|
|
141
|
-
return response.data;
|
|
142
|
-
} catch (error: any) {
|
|
143
|
-
getConfig().LOGGER.error(
|
|
144
|
-
`HTTP request failed ${job.id}: ${error.message}`
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
await publishAudit({
|
|
148
|
-
eventType: "http.request.error",
|
|
149
|
-
properties: {
|
|
150
|
-
connectionId,
|
|
151
|
-
provider,
|
|
152
|
-
endpoint: url,
|
|
153
|
-
method,
|
|
154
|
-
timestamp: Date.now(),
|
|
155
|
-
queueId: job.id,
|
|
156
|
-
reason: "execution_error",
|
|
157
|
-
errorMessage: error.message,
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
throw new Error(`HTTP request failed: ${error.message}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async request(options: {
|
|
166
|
-
method: string;
|
|
167
|
-
url: string;
|
|
168
|
-
body?: any;
|
|
169
|
-
params?: Record<string, any>;
|
|
170
|
-
headers?: Record<string, string>;
|
|
171
|
-
queueOptions?: {
|
|
172
|
-
connectionId: string;
|
|
173
|
-
connectionProvider: string;
|
|
174
|
-
microservice: string;
|
|
175
|
-
};
|
|
176
|
-
}): Promise<IQueueResponse> {
|
|
177
|
-
const { method, url, body, params, headers, queueOptions } = options;
|
|
178
|
-
// Create HttpCallOption object
|
|
179
|
-
const httpCallOption: HttpCallOption = {
|
|
180
|
-
headers,
|
|
181
|
-
body,
|
|
182
|
-
params,
|
|
183
|
-
queueOptions,
|
|
184
|
-
};
|
|
185
|
-
// Call handleRequest with the constructed parameters
|
|
186
|
-
return this.handleRequest(url, method, httpCallOption);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async handleRequest(
|
|
190
|
-
url: string,
|
|
191
|
-
method: string,
|
|
192
|
-
options: HttpCallOption
|
|
193
|
-
): Promise<IQueueResponse> {
|
|
194
|
-
const { connectionId, provider, microservice } =
|
|
195
|
-
JobUtils.extractConnectionDetails(options);
|
|
196
|
-
|
|
197
|
-
// Check rate limit first
|
|
198
|
-
const allowed = await RateLimitUtils.isRateLimitAllowed(
|
|
199
|
-
connectionId,
|
|
200
|
-
provider,
|
|
201
|
-
this.rateLimitConfigs
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
if (!allowed) {
|
|
205
|
-
// Rate limited - queue the request and return immediate response
|
|
206
|
-
return this.handleRateLimitAndQueue(url, method, options);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Not rate limited - process immediately
|
|
210
|
-
getConfig().LOGGER.info(
|
|
211
|
-
`Processing immediately: ${method} ${url} -> ${provider} [${connectionId}]`
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
try {
|
|
215
|
-
// Record the request first
|
|
216
|
-
await RateLimitUtils.recordRequest(connectionId, provider);
|
|
217
|
-
|
|
218
|
-
// Execute the HTTP request
|
|
219
|
-
const response = await axios({
|
|
220
|
-
method: method.toLowerCase(),
|
|
221
|
-
url: url,
|
|
222
|
-
headers: options.headers || {},
|
|
223
|
-
timeout: 30000,
|
|
224
|
-
...(options.body && { data: options.body }),
|
|
225
|
-
...(options.params && { params: options.params }),
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
getConfig().LOGGER.info(
|
|
229
|
-
`HTTP request successful: ${method} ${url} for ${provider}`
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
success: true,
|
|
234
|
-
data: response.data,
|
|
235
|
-
queued: false,
|
|
236
|
-
};
|
|
237
|
-
} catch (error: any) {
|
|
238
|
-
getConfig().LOGGER.error(`HTTP request failed: ${error.message}`);
|
|
239
|
-
|
|
240
|
-
await publishAudit({
|
|
241
|
-
eventType: "http.request.error",
|
|
242
|
-
properties: {
|
|
243
|
-
connectionId,
|
|
244
|
-
provider,
|
|
245
|
-
endpoint: url,
|
|
246
|
-
method,
|
|
247
|
-
timestamp: Date.now(),
|
|
248
|
-
reason: "execution_error",
|
|
249
|
-
errorMessage: error.message,
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
return {
|
|
254
|
-
success: false,
|
|
255
|
-
error: `HTTP request failed: ${error.message}`,
|
|
256
|
-
queued: false,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async shutdown(): Promise<void> {
|
|
262
|
-
getConfig().LOGGER.info("Shutting down HTTP queues...");
|
|
263
|
-
|
|
264
|
-
await Promise.all([
|
|
265
|
-
...Array.from(this.workers.values()).map((worker: any) => worker.close()),
|
|
266
|
-
...Array.from(this.queues.values()).map((queue: any) => queue.close()),
|
|
267
|
-
]);
|
|
268
|
-
|
|
269
|
-
this.workers.clear();
|
|
270
|
-
this.queues.clear();
|
|
271
|
-
this.jobResults.clear();
|
|
272
|
-
getConfig().LOGGER.info("HTTP queues shutdown complete");
|
|
273
|
-
}
|
|
274
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./HybridHttpQueue";
|
package/src/queue/index.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { HttpCallOption } from "../types/http.types";
|
|
2
|
-
import { IQueueResponse } from "./IJobResult";
|
|
3
|
-
|
|
4
|
-
export interface IHybridHttpQueue {
|
|
5
|
-
request(options: {
|
|
6
|
-
method: string;
|
|
7
|
-
url: string;
|
|
8
|
-
body?: any;
|
|
9
|
-
params?: Record<string, any>;
|
|
10
|
-
headers?: Record<string, string>;
|
|
11
|
-
queueOptions?: {
|
|
12
|
-
connectionId: string;
|
|
13
|
-
connectionProvider: string;
|
|
14
|
-
microservice: string;
|
|
15
|
-
};
|
|
16
|
-
}): Promise<IQueueResponse>;
|
|
17
|
-
|
|
18
|
-
handleRequest(
|
|
19
|
-
url: string,
|
|
20
|
-
method: string,
|
|
21
|
-
options: HttpCallOption
|
|
22
|
-
): Promise<IQueueResponse>;
|
|
23
|
-
|
|
24
|
-
shutdown(): Promise<void>;
|
|
25
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface IJobResult {
|
|
2
|
-
result?: any;
|
|
3
|
-
error?: string;
|
|
4
|
-
resolved: boolean;
|
|
5
|
-
timestamp: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface IQueueResponse {
|
|
9
|
-
success: boolean;
|
|
10
|
-
data?: any;
|
|
11
|
-
error?: string;
|
|
12
|
-
queued?: boolean;
|
|
13
|
-
estimatedProcessingTime?: number;
|
|
14
|
-
jobId?: string;
|
|
15
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { Service } from "typedi";
|
|
2
|
-
import { HybridHttpQueue } from "../entities/HybridHttpQueue";
|
|
3
|
-
import { IHybridHttpQueue, IQueueResponse } 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
|
-
params?: Record<string, any>;
|
|
19
|
-
headers?: Record<string, string>;
|
|
20
|
-
queueOptions?: {
|
|
21
|
-
connectionId: string;
|
|
22
|
-
connectionProvider: string;
|
|
23
|
-
microservice: string;
|
|
24
|
-
};
|
|
25
|
-
}): Promise<IQueueResponse> {
|
|
26
|
-
return this.hybridQueue.request(options);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async handleRequest(
|
|
30
|
-
url: string,
|
|
31
|
-
method: string,
|
|
32
|
-
options: HttpCallOption
|
|
33
|
-
): Promise<IQueueResponse> {
|
|
34
|
-
return this.hybridQueue.handleRequest(url, method, options);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async shutdown(): Promise<void> {
|
|
38
|
-
return this.hybridQueue.shutdown();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./QueueService";
|
|
@@ -1,23 +0,0 @@
|
|
|
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
|
-
params?: Record<string, any>;
|
|
17
|
-
headers?: Record<string, string>;
|
|
18
|
-
queueOptions?: {
|
|
19
|
-
connectionId: string;
|
|
20
|
-
connectionProvider: string;
|
|
21
|
-
microservice: string;
|
|
22
|
-
};
|
|
23
|
-
}
|
package/src/queue/types/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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
|
-
removeOnComplete: { age: number; count: number };
|
|
12
|
-
removeOnFail: { age: number; count: number };
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface QueueMetrics {
|
|
16
|
-
totalJobs: number;
|
|
17
|
-
completedJobs: number;
|
|
18
|
-
failedJobs: number;
|
|
19
|
-
pendingJobs: number;
|
|
20
|
-
activeJobs: number;
|
|
21
|
-
}
|
package/src/queue/utils/index.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { Job } from "bullmq";
|
|
2
|
-
import { IJobResult } from "../interfaces";
|
|
3
|
-
|
|
4
|
-
export class JobUtils {
|
|
5
|
-
static async waitForJobCompletion(
|
|
6
|
-
job: Job,
|
|
7
|
-
queueKey: string,
|
|
8
|
-
jobResults: Map<string, IJobResult>
|
|
9
|
-
): Promise<any> {
|
|
10
|
-
return new Promise((resolve, reject) => {
|
|
11
|
-
let timeoutId: NodeJS.Timeout;
|
|
12
|
-
let checkCount = 0;
|
|
13
|
-
const maxChecks = 600;
|
|
14
|
-
|
|
15
|
-
const checkJob = async () => {
|
|
16
|
-
if (++checkCount >= maxChecks) {
|
|
17
|
-
clearTimeout(timeoutId);
|
|
18
|
-
return reject(new Error("Job timeout: Request took too long"));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
const state = await job.getState();
|
|
23
|
-
|
|
24
|
-
if (state === "completed") {
|
|
25
|
-
clearTimeout(timeoutId);
|
|
26
|
-
const memoryResult = jobResults.get(job.id!);
|
|
27
|
-
|
|
28
|
-
if (memoryResult?.resolved) {
|
|
29
|
-
return memoryResult.result !== undefined
|
|
30
|
-
? resolve(memoryResult.result)
|
|
31
|
-
: reject(new Error(memoryResult.error || "Unknown error"));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Fallback to job result
|
|
35
|
-
resolve(await job.returnvalue);
|
|
36
|
-
} else if (state === "failed") {
|
|
37
|
-
clearTimeout(timeoutId);
|
|
38
|
-
const memoryResult = jobResults.get(job.id!);
|
|
39
|
-
const errorMsg =
|
|
40
|
-
memoryResult?.error ||
|
|
41
|
-
(await job.failedReason) ||
|
|
42
|
-
"Unknown error";
|
|
43
|
-
reject(new Error(`Job failed: ${errorMsg}`));
|
|
44
|
-
} else {
|
|
45
|
-
timeoutId = setTimeout(checkJob, checkCount < 10 ? 50 : 100);
|
|
46
|
-
}
|
|
47
|
-
} catch (error: any) {
|
|
48
|
-
clearTimeout(timeoutId);
|
|
49
|
-
reject(new Error(`Error checking job: ${error.message}`));
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
checkJob();
|
|
54
|
-
|
|
55
|
-
// Backup timeout
|
|
56
|
-
setTimeout(() => {
|
|
57
|
-
clearTimeout(timeoutId);
|
|
58
|
-
reject(new Error("Request timeout: Maximum wait time exceeded"));
|
|
59
|
-
}, 60000);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
static extractConnectionDetails(options: any): {
|
|
64
|
-
connectionId: string;
|
|
65
|
-
provider: string;
|
|
66
|
-
microservice: string;
|
|
67
|
-
} {
|
|
68
|
-
const {
|
|
69
|
-
connectionId,
|
|
70
|
-
connectionProvider: provider,
|
|
71
|
-
microservice,
|
|
72
|
-
} = options?.queueOptions ?? {};
|
|
73
|
-
|
|
74
|
-
if (!connectionId) throw new Error("Connection ID not found in options");
|
|
75
|
-
if (!provider) throw new Error("Connection provider not found in options");
|
|
76
|
-
if (!microservice) throw new Error("Microservice not found in options");
|
|
77
|
-
return { connectionId, provider, microservice };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { getConfig } from "../../config/config";
|
|
2
|
-
import { getRedisClient } from "../../db/redis";
|
|
3
|
-
|
|
4
|
-
export class QueueUtils {
|
|
5
|
-
static getQueueKey(
|
|
6
|
-
microservice: string,
|
|
7
|
-
connectionId: string,
|
|
8
|
-
provider: string
|
|
9
|
-
): string {
|
|
10
|
-
return `${microservice}_${provider}_${connectionId}`;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
static getOrCreateQueue(queueKey: string, queues: Map<string, any>): any {
|
|
14
|
-
return (
|
|
15
|
-
queues.get(queueKey) ??
|
|
16
|
-
queues
|
|
17
|
-
.set(
|
|
18
|
-
queueKey,
|
|
19
|
-
new (require("bullmq").Queue)(queueKey, {
|
|
20
|
-
connection: getRedisClient(),
|
|
21
|
-
})
|
|
22
|
-
)
|
|
23
|
-
.get(queueKey)!
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
static getOrCreateWorker(
|
|
28
|
-
queueKey: string,
|
|
29
|
-
workers: Map<string, any>,
|
|
30
|
-
processFunction: (job: any) => Promise<any>,
|
|
31
|
-
jobResults: Map<string, any>
|
|
32
|
-
): void {
|
|
33
|
-
if (workers.has(queueKey)) return;
|
|
34
|
-
|
|
35
|
-
const { Worker } = require("bullmq");
|
|
36
|
-
const worker = new Worker(queueKey, processFunction, {
|
|
37
|
-
connection: getRedisClient(),
|
|
38
|
-
concurrency: 1,
|
|
39
|
-
removeOnComplete: true,
|
|
40
|
-
removeOnFail: true,
|
|
41
|
-
lockDuration: 300000,
|
|
42
|
-
stalledInterval: 60000,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Simplified event handlers
|
|
46
|
-
worker.on("completed", (job: any) => {
|
|
47
|
-
getConfig().LOGGER.info(
|
|
48
|
-
`HTTP request completed: ${job.id} [${queueKey}]`
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// job.returnvalue is the actual result, not a Promise
|
|
52
|
-
const result = job.returnvalue;
|
|
53
|
-
jobResults.set(job.id!, {
|
|
54
|
-
result,
|
|
55
|
-
resolved: true,
|
|
56
|
-
timestamp: Date.now(),
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
worker.on("failed", (job: any, err: any) => {
|
|
61
|
-
getConfig().LOGGER.error(
|
|
62
|
-
`HTTP request failed: ${job?.id} [${queueKey}], Error: ${err.message}`
|
|
63
|
-
);
|
|
64
|
-
jobResults.set(job!.id!, {
|
|
65
|
-
error: err.message,
|
|
66
|
-
resolved: true,
|
|
67
|
-
timestamp: Date.now(),
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
worker.on("error", (err: any) =>
|
|
72
|
-
getConfig().LOGGER.error(`Worker error for ${queueKey}: ${err.message}`)
|
|
73
|
-
);
|
|
74
|
-
worker.on("stalled", (jobId: string) =>
|
|
75
|
-
getConfig().LOGGER.warn(`Job ${jobId} stalled in worker ${queueKey}`)
|
|
76
|
-
);
|
|
77
|
-
worker.on("active", (job: any) =>
|
|
78
|
-
getConfig().LOGGER.info(`HTTP request started: ${job.id} [${queueKey}]`)
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
workers.set(queueKey, worker);
|
|
82
|
-
getConfig().LOGGER.info(`Worker initialized for queue: ${queueKey}`);
|
|
83
|
-
}
|
|
84
|
-
}
|