cloudbroker-sdk 0.3.0 → 0.4.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.
- package/README.md +10 -8
- package/package.json +1 -1
- package/src/client.js +2 -2
- package/src/cloud-wrapper.js +27 -21
- package/src/test.js +10 -4
package/README.md
CHANGED
|
@@ -152,26 +152,28 @@ Em vez de passar `cbLabel` em cada `.send()`, use o middleware: todas as chamada
|
|
|
152
152
|
de nuvem feitas durante a requisição herdam um label de negócio automaticamente
|
|
153
153
|
(via `AsyncLocalStorage`). Ótimo pra agrupar custo por **módulo** ou **rota**.
|
|
154
154
|
|
|
155
|
+
Cada campo (`label`, `product`, `team`) pode ser fixo (string) ou uma função
|
|
156
|
+
`(req) => string`. Isso permite a hierarquia **Grupo (team) → App (product) →
|
|
157
|
+
Trecho (label) → Serviço** na visão de custo.
|
|
158
|
+
|
|
155
159
|
```js
|
|
156
160
|
import express from "express";
|
|
157
161
|
import { CloudBrokerCloud } from "cloudbroker-sdk";
|
|
158
162
|
|
|
159
163
|
const app = express();
|
|
160
164
|
|
|
161
|
-
// label fixo, "MÉTODO /caminho" (default), ou uma função req → label:
|
|
162
165
|
app.use(CloudBrokerCloud.expressMiddleware({
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return "Outros";
|
|
167
|
-
},
|
|
166
|
+
team: (req) => (req.path.startsWith("/api/admin") ? "Plataforma" : "Produto"), // Grupo
|
|
167
|
+
product: (req) => req.path.split("/")[2] || "core", // App
|
|
168
|
+
label: (req) => `${req.method} ${req.path}`, // Trecho
|
|
168
169
|
}));
|
|
169
170
|
|
|
170
171
|
// daqui pra frente, qualquer s3.send(...)/ddb.send(...) dentro da request
|
|
171
|
-
// é atribuído ao
|
|
172
|
+
// é atribuído ao grupo/app/trecho — sem cbLabel manual.
|
|
172
173
|
```
|
|
173
174
|
|
|
174
|
-
Prioridade
|
|
175
|
+
Prioridade de cada campo: explícito no `.send()` (`cbLabel`/`cbProduct`/`cbTeam`) >
|
|
176
|
+
contexto do middleware > valor estático do `wrapAWS` > `null`.
|
|
175
177
|
|
|
176
178
|
## Garantias
|
|
177
179
|
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -37,7 +37,7 @@ export class CloudBrokerClient {
|
|
|
37
37
|
try {
|
|
38
38
|
const payload = {
|
|
39
39
|
...event,
|
|
40
|
-
sdkVersion: "0.
|
|
40
|
+
sdkVersion: "0.4.0",
|
|
41
41
|
sentAt: new Date().toISOString(),
|
|
42
42
|
};
|
|
43
43
|
if (this.#cloudQueue.length < MAX_QUEUE_SIZE) {
|
|
@@ -93,7 +93,7 @@ export class CloudBrokerClient {
|
|
|
93
93
|
try {
|
|
94
94
|
const payload = {
|
|
95
95
|
...event,
|
|
96
|
-
sdkVersion: "0.
|
|
96
|
+
sdkVersion: "0.4.0",
|
|
97
97
|
sentAt: new Date().toISOString(),
|
|
98
98
|
};
|
|
99
99
|
|
package/src/cloud-wrapper.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import { CloudBrokerClient } from "./client.js";
|
|
26
26
|
import { estimateCloudCost } from "./pricing-cloud.js";
|
|
27
|
-
import { cbStore
|
|
27
|
+
import { cbStore } from "./context.js";
|
|
28
28
|
|
|
29
29
|
export class CloudBrokerCloud {
|
|
30
30
|
/**
|
|
@@ -54,13 +54,16 @@ export class CloudBrokerCloud {
|
|
|
54
54
|
client.send = async (command, sendOptions) => {
|
|
55
55
|
const start = Date.now();
|
|
56
56
|
const operation = (command?.constructor?.name || "").replace(/Command$/, "");
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
const
|
|
57
|
+
// Prioridade: explícito no .send() > contexto da requisição (expressMiddleware)
|
|
58
|
+
// > valor estático do wrapAWS. Permite Grupo/App/Trecho por requisição.
|
|
59
|
+
const ctx = cbStore.getStore() || {};
|
|
60
|
+
const cbLabel = sendOptions?.cbLabel || ctx.label || null;
|
|
61
|
+
const evProduct = sendOptions?.cbProduct || ctx.product || product || null;
|
|
62
|
+
const evTeam = sendOptions?.cbTeam || ctx.team || team || null;
|
|
60
63
|
const bytesIn = CloudBrokerCloud.#inputBytes(command?.input);
|
|
61
64
|
|
|
62
|
-
// Passa adiante apenas as options nativas — nunca vaza
|
|
63
|
-
const { cbLabel:
|
|
65
|
+
// Passa adiante apenas as options nativas — nunca vaza campos cb* pro AWS SDK.
|
|
66
|
+
const { cbLabel: _l, cbProduct: _p, cbTeam: _t, ...awsOptions } = sendOptions || {};
|
|
64
67
|
const result = await originalSend(command, Object.keys(awsOptions).length ? awsOptions : undefined);
|
|
65
68
|
|
|
66
69
|
try {
|
|
@@ -73,8 +76,8 @@ export class CloudBrokerCloud {
|
|
|
73
76
|
provider: "aws",
|
|
74
77
|
service,
|
|
75
78
|
operation,
|
|
76
|
-
product:
|
|
77
|
-
team:
|
|
79
|
+
product: evProduct,
|
|
80
|
+
team: evTeam,
|
|
78
81
|
label: cbLabel,
|
|
79
82
|
requests: 1,
|
|
80
83
|
bytesIn,
|
|
@@ -95,26 +98,29 @@ export class CloudBrokerCloud {
|
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
/**
|
|
98
|
-
* Middleware Express que atribui
|
|
99
|
-
* nuvem feitas durante a requisição (via AsyncLocalStorage).
|
|
100
|
-
*
|
|
101
|
+
* Middleware Express que atribui contexto de negócio a todas as chamadas de
|
|
102
|
+
* nuvem feitas durante a requisição (via AsyncLocalStorage). Cada campo pode ser
|
|
103
|
+
* fixo (string) ou uma função (req) => string — ex: mapear rota → módulo/app/grupo.
|
|
104
|
+
* Sem precisar passar cbLabel/cbProduct/cbTeam manual em cada send.
|
|
101
105
|
*
|
|
102
106
|
* @param {object} [options]
|
|
103
|
-
* @param {string|((req)=>string)} [options.label]
|
|
104
|
-
*
|
|
105
|
-
*
|
|
107
|
+
* @param {string|((req)=>string)} [options.label] — trecho (default: "MÉTODO /caminho")
|
|
108
|
+
* @param {string|((req)=>string)} [options.product] — App (sobrescreve o product do wrapAWS)
|
|
109
|
+
* @param {string|((req)=>string)} [options.team] — Grupo (sobrescreve o team do wrapAWS)
|
|
106
110
|
* @returns {(req,res,next)=>void}
|
|
107
111
|
*/
|
|
108
112
|
static expressMiddleware(options = {}) {
|
|
109
|
-
const
|
|
110
|
-
typeof
|
|
111
|
-
typeof
|
|
112
|
-
|
|
113
|
+
const mk = (opt, fallback) =>
|
|
114
|
+
typeof opt === "function" ? opt :
|
|
115
|
+
typeof opt === "string" ? () => opt :
|
|
116
|
+
fallback;
|
|
117
|
+
const resolveLabel = mk(options.label, (req) => `${req.method || ""} ${req.baseUrl || ""}${req.route?.path || req.path || ""}`.trim());
|
|
118
|
+
const resolveProduct = mk(options.product, () => undefined);
|
|
119
|
+
const resolveTeam = mk(options.team, () => undefined);
|
|
113
120
|
|
|
114
121
|
return (req, res, next) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
cbStore.run({ label }, () => next());
|
|
122
|
+
const safe = (fn) => { try { return fn(req); } catch { return undefined; } };
|
|
123
|
+
cbStore.run({ label: safe(resolveLabel), product: safe(resolveProduct), team: safe(resolveTeam) }, () => next());
|
|
118
124
|
};
|
|
119
125
|
}
|
|
120
126
|
|
package/src/test.js
CHANGED
|
@@ -154,18 +154,24 @@ assert.ok(ev.costUSD > 0, "deve ter custo estimado");
|
|
|
154
154
|
console.log("✅ wrapAWS: evento capturado, cbLabel não vazou, resultado preservado");
|
|
155
155
|
|
|
156
156
|
// 3) expressMiddleware — label do contexto (ALS) entra sem cbLabel explícito
|
|
157
|
-
const mw = CloudBrokerCloud.expressMiddleware({
|
|
157
|
+
const mw = CloudBrokerCloud.expressMiddleware({
|
|
158
|
+
label: (req) => `modulo:${req.path}`,
|
|
159
|
+
product: (req) => (req.path.startsWith("/api/billing") ? "Billing" : "Outros"),
|
|
160
|
+
team: "Plataforma",
|
|
161
|
+
});
|
|
158
162
|
const fakeReq = { method: "GET", path: "/api/billing" };
|
|
159
163
|
await new Promise((resolve) => {
|
|
160
164
|
mw(fakeReq, {}, async () => {
|
|
161
|
-
await s3.send(new PutObjectCommand({ Bucket: "b", Key: "k", Body: "x" })); // sem
|
|
165
|
+
await s3.send(new PutObjectCommand({ Bucket: "b", Key: "k", Body: "x" })); // sem cb* — herda do contexto
|
|
162
166
|
resolve();
|
|
163
167
|
});
|
|
164
168
|
});
|
|
165
169
|
await new Promise((r) => setTimeout(r, 50));
|
|
166
170
|
const ctxEvent = captured[captured.length - 1].events[0];
|
|
167
|
-
assert.strictEqual(ctxEvent.label,
|
|
168
|
-
|
|
171
|
+
assert.strictEqual(ctxEvent.label, "modulo:/api/billing", `label do contexto esperado, veio ${ctxEvent.label}`);
|
|
172
|
+
assert.strictEqual(ctxEvent.product, "Billing", `product do contexto esperado, veio ${ctxEvent.product}`);
|
|
173
|
+
assert.strictEqual(ctxEvent.team, "Plataforma", `team do contexto esperado, veio ${ctxEvent.team}`);
|
|
174
|
+
console.log("✅ expressMiddleware: label/product/team do contexto aplicados sem cb* explícito");
|
|
169
175
|
|
|
170
176
|
globalThis.fetch = realFetch;
|
|
171
177
|
console.log("\n🎉 Todos os testes passaram.");
|