@oondemand/oon-core-back 0.3.64 → 0.3.66
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/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const express = require("express");
|
|
4
|
+
const mongoose = require("mongoose");
|
|
4
5
|
const GenericError = require("../../core/errors/GenericError");
|
|
5
6
|
const auth = require("../../core/middlewares/auth");
|
|
6
7
|
const { rbac } = require("../../core/middlewares/rbac");
|
|
7
8
|
const { asyncHandler } = require("../../core/utils/helpers");
|
|
8
|
-
const { getIntegrationModels } = require("./models");
|
|
9
|
+
const { getIntegrationModels, OUTBOX_STATUSES } = require("./models");
|
|
9
10
|
const { getIntegrationProvider, listIntegrationProviders } = require("./registry");
|
|
10
11
|
const {
|
|
11
12
|
archiveIntegrationTicket,
|
|
@@ -33,6 +34,48 @@ function pagination(query = {}) {
|
|
|
33
34
|
return { limit, page, skip: (page - 1) * limit };
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
function escapeRegExp(value) {
|
|
38
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildQueueFilter(query = {}, options = {}) {
|
|
42
|
+
const includeStatus = options.includeStatus !== false;
|
|
43
|
+
const filter = {};
|
|
44
|
+
for (const field of ["provider", "resource", "operation", "aggregateType", "aggregateId"]) {
|
|
45
|
+
if (query[field]) filter[field] = String(query[field]);
|
|
46
|
+
}
|
|
47
|
+
if (includeStatus && query.status) filter.status = String(query.status);
|
|
48
|
+
|
|
49
|
+
const search = String(query.search || "").trim().slice(0, 160);
|
|
50
|
+
if (search) {
|
|
51
|
+
const pattern = new RegExp(escapeRegExp(search), "i");
|
|
52
|
+
const clauses = [
|
|
53
|
+
{ handler: pattern },
|
|
54
|
+
{ resource: pattern },
|
|
55
|
+
{ operation: pattern },
|
|
56
|
+
{ aggregateType: pattern },
|
|
57
|
+
{ aggregateId: pattern },
|
|
58
|
+
{ idempotencyKey: pattern },
|
|
59
|
+
];
|
|
60
|
+
if (mongoose.isValidObjectId(search)) clauses.unshift({ _id: new mongoose.Types.ObjectId(search) });
|
|
61
|
+
filter.$or = clauses;
|
|
62
|
+
}
|
|
63
|
+
return filter;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function queueStatusSummary(Outbox, filter = {}) {
|
|
67
|
+
const rows = await Outbox.aggregate([
|
|
68
|
+
{ $match: filter },
|
|
69
|
+
{ $group: { _id: "$status", count: { $sum: 1 } } },
|
|
70
|
+
]);
|
|
71
|
+
const statuses = Object.fromEntries(OUTBOX_STATUSES.map((status) => [status, 0]));
|
|
72
|
+
for (const row of rows) statuses[String(row._id)] = Number(row.count || 0);
|
|
73
|
+
return {
|
|
74
|
+
total: Object.values(statuses).reduce((sum, count) => sum + Number(count || 0), 0),
|
|
75
|
+
statuses,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
36
79
|
function requireProviderExtension(provider, method, message) {
|
|
37
80
|
if (typeof provider[method] !== "function") {
|
|
38
81
|
throw new GenericError(message || `O provedor ${provider.label} não oferece ${method}.`, {
|
|
@@ -144,16 +187,16 @@ function buildIntegrationRouter() {
|
|
|
144
187
|
router.get("/queue", rbac(READ_ROLES), asyncHandler(async (req, res) => {
|
|
145
188
|
const { Outbox } = getIntegrationModels();
|
|
146
189
|
const { limit, page, skip } = pagination(req.query);
|
|
147
|
-
const filter =
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
const [results, totalItems] = await Promise.all([
|
|
190
|
+
const filter = buildQueueFilter(req.query);
|
|
191
|
+
const summaryFilter = buildQueueFilter(req.query, { includeStatus: false });
|
|
192
|
+
const [results, totalItems, summary] = await Promise.all([
|
|
152
193
|
Outbox.find(filter).sort({ createdAt: -1 }).skip(skip).limit(limit).lean(),
|
|
153
194
|
Outbox.countDocuments(filter),
|
|
195
|
+
queueStatusSummary(Outbox, summaryFilter),
|
|
154
196
|
]);
|
|
155
197
|
res.json({
|
|
156
198
|
results: sanitizeIntegrationValue(results),
|
|
199
|
+
summary,
|
|
157
200
|
pagination: {
|
|
158
201
|
currentPage: page,
|
|
159
202
|
totalItems,
|
|
@@ -222,4 +265,4 @@ function buildIntegrationRouter() {
|
|
|
222
265
|
return router;
|
|
223
266
|
}
|
|
224
267
|
|
|
225
|
-
module.exports = { buildIntegrationRouter };
|
|
268
|
+
module.exports = { buildIntegrationRouter, buildQueueFilter, queueStatusSummary };
|
|
@@ -95,6 +95,22 @@ function responseError(body, statusCode) {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
function normalizedStringList(value) {
|
|
99
|
+
const values = Array.isArray(value) ? value : value === undefined || value === null ? [] : [value];
|
|
100
|
+
return values.map((item) => String(item).trim()).filter(Boolean);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isConfiguredEmptyResult(callDefinition = {}, body = {}) {
|
|
104
|
+
const code = String(body?.faultcode ?? body?.code ?? body?.codigo_status ?? "").trim();
|
|
105
|
+
const description = String(
|
|
106
|
+
body?.faultstring ?? body?.description ?? body?.descricao_status ?? "",
|
|
107
|
+
).trim();
|
|
108
|
+
const codes = normalizedStringList(callDefinition.emptyResultFaultCodes);
|
|
109
|
+
const messages = normalizedStringList(callDefinition.emptyResultMessages);
|
|
110
|
+
return (code && codes.includes(code))
|
|
111
|
+
|| (description && messages.some((message) => description.includes(message)));
|
|
112
|
+
}
|
|
113
|
+
|
|
98
114
|
function retryableTransportError(error) {
|
|
99
115
|
const statusCode = Number(error?.response?.status || error?.statusCode || 0);
|
|
100
116
|
return RETRYABLE_HTTP.has(statusCode)
|
|
@@ -166,9 +182,10 @@ function createOmieClient(options = {}) {
|
|
|
166
182
|
app_secret: appSecret,
|
|
167
183
|
param: Array.isArray(resolvedParam) ? resolvedParam : [resolvedParam],
|
|
168
184
|
};
|
|
185
|
+
const callMaxAttempts = Math.max(1, Number(callDefinition.maxAttempts ?? maxAttempts));
|
|
169
186
|
let lastError;
|
|
170
187
|
|
|
171
|
-
for (let attempt = 1; attempt <=
|
|
188
|
+
for (let attempt = 1; attempt <= callMaxAttempts; attempt += 1) {
|
|
172
189
|
const startedAt = Date.now();
|
|
173
190
|
const trace = {
|
|
174
191
|
endpoint: callDefinition.endpoint,
|
|
@@ -185,14 +202,18 @@ function createOmieClient(options = {}) {
|
|
|
185
202
|
});
|
|
186
203
|
const status = Number(response?.status || 200);
|
|
187
204
|
const data = response?.data ?? {};
|
|
205
|
+
const emptyResult = isConfiguredEmptyResult(callDefinition, data);
|
|
188
206
|
trace.httpStatus = status;
|
|
189
207
|
trace.response = sanitizeIntegrationValue(data);
|
|
190
208
|
trace.durationMs = Date.now() - startedAt;
|
|
191
|
-
|
|
192
|
-
if (
|
|
209
|
+
trace.emptyResult = emptyResult;
|
|
210
|
+
if (!emptyResult) {
|
|
211
|
+
const normalized = responseError(data, status);
|
|
212
|
+
if (normalized) throw normalized;
|
|
213
|
+
}
|
|
193
214
|
trace.status = "success";
|
|
194
215
|
context.recordRequest?.(trace);
|
|
195
|
-
return { data, trace };
|
|
216
|
+
return { data, trace, emptyResult };
|
|
196
217
|
} catch (error) {
|
|
197
218
|
const normalized = error instanceof OmieApiError
|
|
198
219
|
? error
|
|
@@ -209,7 +230,7 @@ function createOmieClient(options = {}) {
|
|
|
209
230
|
context.recordRequest?.(trace);
|
|
210
231
|
normalized.trace = trace;
|
|
211
232
|
lastError = normalized;
|
|
212
|
-
if (!normalized.retryable || attempt >=
|
|
233
|
+
if (!normalized.retryable || attempt >= callMaxAttempts) throw normalized;
|
|
213
234
|
const delay = Math.min(5_000, retryInitialMs * (2 ** (attempt - 1)));
|
|
214
235
|
await wait(delay);
|
|
215
236
|
}
|
|
@@ -253,8 +274,9 @@ module.exports = {
|
|
|
253
274
|
OmieApiError,
|
|
254
275
|
createOmieClient,
|
|
255
276
|
getPath,
|
|
277
|
+
isConfiguredEmptyResult,
|
|
256
278
|
normalizeEndpoint,
|
|
257
279
|
resolveCallParam,
|
|
258
280
|
resolveTemplate,
|
|
259
281
|
responseError,
|
|
260
|
-
};
|
|
282
|
+
};
|