cloudbroker-sdk 0.5.0 → 0.6.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.
@@ -4,7 +4,16 @@ on:
4
4
  push:
5
5
  branches: [main]
6
6
  paths:
7
+ # Sem barra inicial: filtros de path do GitHub Actions são relativos à
8
+ # raiz do repo e '/package.json' não casa com nada — o workflow deixou de
9
+ # disparar por completo (o 0.6.0 foi mergeado e nunca publicado).
10
+ # 'package.json' já cobre só a raiz: sem '**', o glob não atravessa
11
+ # diretórios, que era a intenção original de ancorar.
7
12
  - 'package.json'
13
+ # Escape hatch: quando o filtro acima não dispara (foi o caso do 0.6.0, que
14
+ # entrou no main sem publicar), dá pra publicar a versão que está no main sem
15
+ # precisar de um bump artificial só pra acionar o gatilho.
16
+ workflow_dispatch:
8
17
 
9
18
  jobs:
10
19
  publish:
@@ -22,6 +31,15 @@ jobs:
22
31
  node-version: 20
23
32
  registry-url: 'https://registry.npmjs.org'
24
33
 
34
+ # Gate de teste antes do publish — este pacote roda DENTRO da aplicação
35
+ # de produção do cliente; publicar sem rodar a suíte já era um risco
36
+ # aberto (nenhum CI de teste existia neste repo).
37
+ - name: Instalar dependências de teste
38
+ run: npm ci || npm install
39
+
40
+ - name: Rodar testes
41
+ run: npm test
42
+
25
43
  - name: Publish to npm
26
44
  run: npm publish
27
45
  env:
package/README.md CHANGED
@@ -106,14 +106,17 @@ CloudBrokerAI.trackDeploy("cb-key-xxxx", {
106
106
  });
107
107
  ```
108
108
 
109
- ## Custo de nuvem a nível de código (MVP)
109
+ ## Cost per Feature — Rastreie Custo por Feature
110
110
 
111
- Atribua custo de **AWS** a um trecho de código / produto. O SDK envolve um client
112
- do AWS SDK v3 e, a cada `.send()`, estima o custo (requests + transferência) e
111
+ Atribua custo de **AWS** e **Azure** a cada feature/módulo sua. O SDK envolve um
112
+ client do provedor e, a cada chamada, estima o custo (requests + transferência) e
113
113
  registra um evento — mesmo padrão fire-and-forget do wrapper de IA.
114
114
 
115
- > MVP: AWS apenas, serviços **S3** e **DynamoDB**, região `us-east-2`. Armazenamento
116
- > GB-mês, Azure e atribuição automática (stack-trace) estão fora do MVP.
115
+ **👉 [Guia Completo](./docs/COST_PER_FEATURE.md)** — Setup (5 min), exemplos, FAQ.
116
+
117
+ > Escopo atual — AWS: **S3** e **DynamoDB** (`us-east-2`); Azure: **Blob Storage**
118
+ > e **Cosmos DB** (`eastus2`). Armazenamento GB-mês e atribuição automática
119
+ > (stack-trace) estão fora do MVP.
117
120
 
118
121
  ```js
119
122
  import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
@@ -146,6 +149,81 @@ Cada chamada gera um evento `POST /api/cloud/events` com `service`, `operation`,
146
149
  `product`, `team`, `label`, `requests`, `bytesIn`/`bytesOut`, `costUSD` estimado e
147
150
  `latencyMs`. O `cbLabel` nunca é repassado ao AWS SDK.
148
151
 
152
+ ### Azure — Blob Storage e Cosmos DB
153
+
154
+ O Azure SDK não tem um `.send()` único como o AWS SDK v3: cada sub-client tem
155
+ métodos próprios. Por isso o `wrapAzure` intercepta a **cadeia de sub-clients** —
156
+ as fábricas (`getContainerClient`, `database`/`container`) devolvem sub-clients já
157
+ embrulhados, e só os métodos de data-plane conhecidos são rastreados. Qualquer
158
+ método fora dessa lista passa intocado.
159
+
160
+ ```js
161
+ import { BlobServiceClient } from "@azure/storage-blob";
162
+ import { CloudBrokerCloud } from "cloudbroker-sdk";
163
+
164
+ const blobSvc = CloudBrokerCloud.wrapAzure(
165
+ BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION),
166
+ { apiKey: "cb-key-xxxx", product: "checkout", team: "mobile" },
167
+ );
168
+
169
+ // label opcional dentro do objeto de options nativo do método
170
+ await blobSvc
171
+ .getContainerClient("docs")
172
+ .getBlockBlobClient("nf.pdf")
173
+ .uploadData(buf, { cbLabel: "upload-nota-fiscal" });
174
+ ```
175
+
176
+ Cosmos DB usa o **`requestCharge` (RUs) real** que o servidor devolve em cada
177
+ resposta — custo por unidade cobrada de verdade, não aproximação por request:
178
+
179
+ ```js
180
+ import { CosmosClient } from "@azure/cosmos";
181
+
182
+ const cosmos = CloudBrokerCloud.wrapAzure(new CosmosClient(conn), {
183
+ apiKey: "cb-key-xxxx", product: "checkout",
184
+ });
185
+ const orders = cosmos.database("loja").container("pedidos");
186
+
187
+ await orders.items.create({ id: "123" }, { cbLabel: "criar-pedido" });
188
+ await orders.items.query("SELECT * FROM c").fetchAll(); // RUs do query também contam
189
+ ```
190
+
191
+ O `expressMiddleware` (abaixo) funciona igual pros dois provedores — mesma
192
+ prioridade de `label`/`product`/`team`, sem precisar de `cbLabel` manual.
193
+
194
+ > **Precisão e premissas — leia antes de tratar o número como fechado:**
195
+ >
196
+ > - **Cosmos:** as RUs são as **reais** devolvidas pelo servidor em cada resposta
197
+ > (`requestCharge`). O **preço por RU** assume o modelo **serverless**. Em
198
+ > *throughput provisionado* (manual ou autoscale) a Azure cobra RU/s reservada
199
+ > por hora, independentemente do consumo — esse modelo ainda não é representado,
200
+ > então o custo calculado não corresponde à fatura nesse caso.
201
+ > - **Blob:** estimado por classe de operação (write/read/list), assumindo tier
202
+ > **Hot** e redundância **LRS**. Cool/Archive e GRS/ZRS têm outro preço.
203
+ > - **Transferência de saída:** cobrada sobre todo `bytesOut`. A Azure não cobra
204
+ > saída intra-região e concede franquia mensal — para um app rodando na mesma
205
+ > região do Storage, este termo **superestima**.
206
+ > - **Região:** só `eastus2` tem tabela de preço. Em outra região o evento é
207
+ > registrado com `costUSD: null` e `billingUnit: "*-region-unpriced"` — a
208
+ > operação aparece no relatório, mas sem custo inventado.
209
+ > - **Reconciliação:** a tela compara o estimado contra o custo real do Cost
210
+ > Management e mostra o **desvio**; o ajuste da tabela é manual (não há
211
+ > recalibração automática).
212
+ > - **Listagem:** `listBlobsFlat`/`listContainers` devolvem iterator preguiçoso —
213
+ > o evento só é emitido quando uma página é de fato buscada. Percorrendo com
214
+ > `for await`, contamos **1 operação** mesmo que o SDK pagine internamente;
215
+ > use `.byPage()` para contagem por página.
216
+ > - **Cosmos `for await` direto** (`getAsyncIterator`) não é instrumentado —
217
+ > use `fetchAll()`/`fetchNext()`.
218
+ > - **Delete** no tier Hot é grátis: sai com custo `0` e
219
+ > `billingUnit: "blob-free"`, sem sumir do relatório.
220
+ >
221
+ > ⚠️ **Ainda não calibrado contra uma fatura Azure real.** A estrutura é
222
+ > exercitada contra os SDKs oficiais (`@azure/storage-blob`, `@azure/cosmos`) na
223
+ > suíte de testes, mas os valores da tabela de preço Azure não foram conferidos
224
+ > contra a página oficial nem contra uma fatura. Trate o custo Azure como ordem
225
+ > de grandeza até rodar a reconciliação na sua conta.
226
+
149
227
  ### Atribuição automática por requisição (Express)
150
228
 
151
229
  Em vez de passar `cbLabel` em cada `.send()`, use o middleware: todas as chamadas
@@ -0,0 +1,296 @@
1
+ # CloudBroker SDK — Rastreie Custo por Feature
2
+
3
+ ## O Que É?
4
+
5
+ Instrumentar sua aplicação com o CloudBroker SDK permite rastrear **quanto cada feature/módulo seu custa em AWS e Azure**.
6
+
7
+ ```javascript
8
+ // Seu código
9
+ await s3.putObject({ ... }, { cbLabel: 'invoice-generation' });
10
+ // CloudBroker registra: feature 'invoice-generation' custou $0.00042
11
+ ```
12
+
13
+ **Result:** Dashboard mostra "invoice-generation: $X/mês".
14
+
15
+ ---
16
+
17
+ ## Setup Rápido (5 minutos)
18
+
19
+ ### 1. Instalar
20
+
21
+ ```bash
22
+ npm install cloudbroker-sdk
23
+ ```
24
+
25
+ ### 2. Gerar API Key
26
+
27
+ 1. Acesse https://app.cloudbroker.app.br
28
+ 2. Configurações → API Keys
29
+ 3. Criar nova key do tipo **SDK** (só Admin pode criar keys SDK)
30
+ 4. Copiar: `cb_live_...` — **o valor é exibido uma única vez**; depois só o
31
+ prefixo mascarado fica visível. Se perder, gere outra.
32
+
33
+ ### 3. Instrumentar Backend
34
+
35
+ ```javascript
36
+ import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
37
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
38
+ import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
39
+ import { CloudBrokerCloud } from "cloudbroker-sdk";
40
+
41
+ // S3
42
+ const s3 = CloudBrokerCloud.wrapAWS(new S3Client({ region: "us-east-2" }), {
43
+ apiKey: process.env.CLOUDBROKER_API_KEY,
44
+ product: "checkout-service", // seu app
45
+ team: "payments", // seu squad
46
+ });
47
+
48
+ // DynamoDB
49
+ const dynamodb = CloudBrokerCloud.wrapAWS(
50
+ DynamoDBDocumentClient.from(new DynamoDBClient({ region: "us-east-2" })),
51
+ { apiKey: process.env.CLOUDBROKER_API_KEY, product: "checkout-service", team: "payments" }
52
+ );
53
+ ```
54
+
55
+ ### 4. Adicionar Labels
56
+
57
+ ```javascript
58
+ // S3 upload com label
59
+ await s3.send(new PutObjectCommand({
60
+ Bucket: 'invoices',
61
+ Key: `${orderId}.pdf`,
62
+ Body: pdfBuffer,
63
+ }), {
64
+ cbLabel: 'invoice-generation', // ← Identifica a feature
65
+ });
66
+
67
+ // DynamoDB insert com label
68
+ await dynamodb.send(new PutCommand({
69
+ TableName: 'Orders',
70
+ Item: { orderId, status: 'pending' },
71
+ }), {
72
+ cbLabel: 'order-processing', // ← Identifica a feature
73
+ });
74
+ ```
75
+
76
+ ### 5. Ver Custos
77
+
78
+ Dashboard: **CloudBroker App → Custo por Código**
79
+
80
+ ```
81
+ Feature: order-processing
82
+ Requests: 3,456
83
+ Total: $87.50
84
+ Per Request: $0.0253 ✅
85
+
86
+ Feature: invoice-generation
87
+ Requests: 1,234
88
+ Total: $10.20
89
+ Per Request: $0.00828 ✅
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Azure — Blob Storage e Cosmos DB
95
+
96
+ O SDK da Azure não tem um `.send()` único como o AWS SDK v3, então o `wrapAzure`
97
+ intercepta a **cadeia de sub-clients**: as fábricas (`getContainerClient`,
98
+ `database`/`container`) devolvem sub-clients já instrumentados.
99
+
100
+ ```javascript
101
+ import { BlobServiceClient } from "@azure/storage-blob";
102
+ import { CloudBrokerCloud } from "cloudbroker-sdk";
103
+
104
+ const blobSvc = CloudBrokerCloud.wrapAzure(
105
+ BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION),
106
+ { apiKey: "cb_live_...", product: "checkout", team: "mobile" },
107
+ );
108
+
109
+ await blobSvc
110
+ .getContainerClient("docs")
111
+ .getBlockBlobClient("nf.pdf")
112
+ .uploadData(buf, { cbLabel: "upload-nota-fiscal" });
113
+ ```
114
+
115
+ Cosmos DB usa o `requestCharge` (RUs) **real** devolvido pelo servidor:
116
+
117
+ ```javascript
118
+ import { CosmosClient } from "@azure/cosmos";
119
+
120
+ const cosmos = CloudBrokerCloud.wrapAzure(new CosmosClient(conn), {
121
+ apiKey: "cb_live_...", product: "checkout",
122
+ });
123
+ const pedidos = cosmos.database("loja").container("pedidos");
124
+
125
+ await pedidos.items.create({ id: "123" }, { cbLabel: "criar-pedido" });
126
+ await pedidos.items.query("SELECT * FROM c").fetchAll();
127
+ ```
128
+
129
+ O `expressMiddleware` funciona igual para AWS e Azure.
130
+
131
+ **Premissas que mudam o número** (detalhe completo no
132
+ [README](../README.md#cost-per-feature--rastreie-custo-por-feature)):
133
+
134
+ | Item | Situação |
135
+ |---|---|
136
+ | Cosmos — RUs | Reais (`requestCharge` do servidor) |
137
+ | Cosmos — preço por RU | Assume **serverless**. Throughput provisionado não é modelado |
138
+ | Blob | Estimado por classe de operação; assume tier **Hot** + **LRS** |
139
+ | Egress | Cobrado sobre todo `bytesOut` (a Azure não cobra intra-região) |
140
+ | Região | Só `eastus2` tem preço; outras saem com `costUSD: null` |
141
+ | `for await` no Cosmos | Não instrumentado — use `fetchAll()`/`fetchNext()` |
142
+ | Calibração | ⚠️ **Não conferido contra fatura Azure real ainda** |
143
+
144
+ ---
145
+
146
+ ## Convenção de Labels
147
+
148
+ Use nomes **descritivos** que refletem features/módulos:
149
+
150
+ | Feature | Label | Exemplos de Operações |
151
+ |---------|-------|----------------------|
152
+ | Checkout | `order-processing` | Criar pedido, persistir dados |
153
+ | Geração de Invoice | `invoice-generation` | Upload de PDF, armazenamento |
154
+ | Detecção de Fraude | `fraud-detection` | Análise de padrões, lookup histórico |
155
+ | Analytics | `analytics-tracking` | Coleta de eventos, agregação |
156
+ | Admin Panel | `admin-reports` | Geração de relatórios, queries |
157
+
158
+ **Regras:**
159
+ - ✅ Use labels **finitos** (não criar um label por request)
160
+ - ✅ Labels **descritivos** (não `x1`, `x2`, etc)
161
+ - ✅ Use **kebab-case** (`order-processing`, não `orderProcessing`)
162
+
163
+ ❌ **EVITAR:**
164
+ ```javascript
165
+ // Ruim: label infinito (1 por request)
166
+ cbLabel: `order-${order.id}`
167
+
168
+ // Ruim: não descritivo
169
+ cbLabel: 'x1'
170
+
171
+ // Bom: finito e descritivo
172
+ cbLabel: 'order-processing'
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Exemplo Completo
178
+
179
+ ```javascript
180
+ import express from 'express';
181
+ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
182
+ import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';
183
+ import { CloudBrokerCloud } from 'cloudbroker-sdk';
184
+
185
+ const app = express();
186
+
187
+ // Setup
188
+ const s3 = CloudBrokerCloud.wrapAWS(new S3Client(), {
189
+ apiKey: process.env.CLOUDBROKER_API_KEY,
190
+ product: 'checkout-service',
191
+ team: 'payments',
192
+ });
193
+
194
+ const dynamodb = CloudBrokerCloud.wrapAWS(
195
+ DynamoDBDocumentClient.from(new DynamoDBClient()),
196
+ { apiKey: process.env.CLOUDBROKER_API_KEY, product: 'checkout-service', team: 'payments' }
197
+ );
198
+
199
+ // POST /orders — cria pedido com 2 features diferentes
200
+ app.post('/orders', async (req, res) => {
201
+ const order = { orderId: `order-${Date.now()}`, ...req.body };
202
+
203
+ // Feature 1: Persistir pedido
204
+ await dynamodb.send(new PutCommand({
205
+ TableName: 'Orders',
206
+ Item: order,
207
+ }), {
208
+ cbLabel: 'order-processing',
209
+ });
210
+
211
+ // Feature 2: Gerar invoice
212
+ await s3.send(new PutObjectCommand({
213
+ Bucket: 'invoices',
214
+ Key: `${order.orderId}.json`,
215
+ Body: JSON.stringify(order),
216
+ }), {
217
+ cbLabel: 'invoice-generation',
218
+ });
219
+
220
+ res.json({ ok: true, orderId: order.orderId });
221
+ });
222
+
223
+ app.listen(3000);
224
+ ```
225
+
226
+ **Result no Dashboard:**
227
+ - `order-processing`: $X/mês
228
+ - `invoice-generation`: $Y/mês
229
+
230
+ ---
231
+
232
+ ## FAQ
233
+
234
+ **P: Quanto tempo leva pra aparecer no dashboard?**
235
+ A: ~5 minutos (batch em DynamoDB).
236
+
237
+ **P: E se não colocar label?**
238
+ A: Ainda rastreia como `null` (agrupa sem label).
239
+
240
+ **P: Posso mudar o label depois?**
241
+ A: Não — é histórico. Use um novo label a partir de agora.
242
+
243
+ **P: Qual é o overhead?**
244
+ A: <1% CPU (fire-and-forget, não bloqueia requests).
245
+
246
+ **P: Funciona em todas as regiões?**
247
+ A: Hoje há tabela de preço para `us-east-2` (AWS) e `eastus2` (Azure). Em outra
248
+ região a operação é registrada com `costUSD: null` e `billingUnit`
249
+ `*-region-unpriced` — aparece no relatório, mas sem custo estimado (preferimos
250
+ não exibir o preço de outra região como se fosse o seu).
251
+
252
+ **P: E se der erro no rastreamento?**
253
+ A: Silencioso — seu request continua normal (fail-soft).
254
+
255
+ ---
256
+
257
+ ## Troubleshooting
258
+
259
+ ### Nenhum dado aparecendo no dashboard?
260
+
261
+ 1. ✅ API Key configurada?
262
+ ```bash
263
+ echo $CLOUDBROKER_API_KEY
264
+ ```
265
+
266
+ 2. ✅ CloudBrokerCloud.wrapAWS() chamado?
267
+ ```javascript
268
+ const s3 = CloudBrokerCloud.wrapAWS(new S3Client(), { apiKey, product, team });
269
+ ```
270
+
271
+ 3. ✅ cbLabel adicionado?
272
+ ```javascript
273
+ await s3.send(cmd, { cbLabel: 'seu-label' });
274
+ ```
275
+
276
+ 4. ✅ Esperou 5+ minutos?
277
+ (CloudBroker faz batch, não é real-time)
278
+
279
+ ### Custo aparecendo como $0?
280
+
281
+ - Normal no MVP (estimativa simplificada, reconciliação depois)
282
+ - Operações muito baratas podem ficar abaixo do centavo
283
+
284
+ ---
285
+
286
+ ## Próximos Passos
287
+
288
+ - **Exemplo funcional:** Veja `example-checkout-app` no repo
289
+ - **Mais Serviços:** Lambda, SQS, SNS; Azure Functions, Service Bus (roadmap)
290
+ - **OpenTelemetry:** Rastrear custom operations (futuro)
291
+
292
+ ---
293
+
294
+ ## Suporte
295
+
296
+ Dúvidas? Abra issue no [GitHub](https://github.com/cloudbroker-app/cloudbroker-sdk).
@@ -0,0 +1,13 @@
1
+ # AWS Configuration
2
+ AWS_REGION=us-east-2
3
+ AWS_ACCESS_KEY_ID=your-access-key
4
+ AWS_SECRET_ACCESS_KEY=your-secret-key
5
+
6
+ # CloudBroker Configuration
7
+ CLOUDBROKER_API_KEY=sk_1234...
8
+ CLOUDBROKER_PRODUCT=checkout-example
9
+ CLOUDBROKER_TEAM=platform
10
+
11
+ # App Configuration
12
+ PORT=3000
13
+ NODE_ENV=development
@@ -0,0 +1,225 @@
1
+ # CloudBroker SDK Example — Checkout Service
2
+
3
+ Exemplo funcional de como instrumentar um serviço de checkout com CloudBroker SDK para rastrear custo por feature.
4
+
5
+ ## O Que Faz
6
+
7
+ Este app de exemplo mostra 5 features sendo rastreadas:
8
+
9
+ | Feature | Route | Operation | Label |
10
+ |---------|-------|-----------|-------|
11
+ | **Order Processing** | POST /orders | DynamoDB PutItem | `order-processing` |
12
+ | **Invoice Generation** | POST /orders | S3 PutObject | `invoice-generation` |
13
+ | **Order Retrieval** | GET /orders/:id | DynamoDB GetItem | `order-retrieval` |
14
+ | **Fraud Detection** | POST /verify-fraud | DynamoDB GetItem | `fraud-detection` |
15
+ | **Analytics Tracking** | POST /analytics | DynamoDB PutItem | `analytics-tracking` |
16
+
17
+ Cada feature rastreia seu custo AWS separadamente — depois você pode ver no dashboard CloudBroker quanto cada uma custou.
18
+
19
+ ## Setup (5 minutos)
20
+
21
+ ### 1. Instalar Dependências
22
+
23
+ ```bash
24
+ npm install
25
+ ```
26
+
27
+ ### 2. Configurar Credenciais
28
+
29
+ ```bash
30
+ cp .env.example .env
31
+ # Editar .env com suas credenciais AWS + CloudBroker API Key
32
+ ```
33
+
34
+ **Campos obrigatórios:**
35
+ - `AWS_ACCESS_KEY_ID` — sua chave AWS
36
+ - `AWS_SECRET_ACCESS_KEY` — seu secret AWS
37
+ - `CLOUDBROKER_API_KEY` — sua API key CloudBroker (obter em app.cloudbroker.app.br)
38
+
39
+ ### 3. Rodar
40
+
41
+ ```bash
42
+ npm start
43
+ # Saída: ✅ Checkout Example App running on http://localhost:3000
44
+ ```
45
+
46
+ ### 4. Testar
47
+
48
+ ```bash
49
+ # Criar pedido (rastreia 2 features)
50
+ curl -X POST http://localhost:3000/orders \
51
+ -H "Content-Type: application/json" \
52
+ -d '{
53
+ "customerId": "cust-123",
54
+ "items": [{"name": "Widget", "price": 99.99}],
55
+ "total": 99.99
56
+ }'
57
+
58
+ # Resposta:
59
+ # {
60
+ # "ok": true,
61
+ # "orderId": "order-1719600600000",
62
+ # "message": "Order created successfully!..."
63
+ # }
64
+ ```
65
+
66
+ ### 5. Ver Custos
67
+
68
+ 1. Acesse https://app.cloudbroker.app.br
69
+ 2. Vá a **Custo por Código**
70
+ 3. Você verá os labels: `order-processing`, `invoice-generation`, etc
71
+ 4. **Nota:** Demora ~5 minutos pro CloudBroker processar os dados
72
+
73
+ ## Exemplos de Uso
74
+
75
+ ### Criar Pedido
76
+
77
+ ```bash
78
+ curl -X POST http://localhost:3000/orders \
79
+ -H "Content-Type: application/json" \
80
+ -d '{
81
+ "customerId": "user-456",
82
+ "items": [{"name": "Widget A"}, {"name": "Widget B"}],
83
+ "total": 250.00
84
+ }'
85
+ ```
86
+
87
+ **O que acontece:**
88
+ 1. `order-processing`: DynamoDB registra o pedido
89
+ 2. `invoice-generation`: S3 armazena o JSON da invoice
90
+
91
+ ### Recuperar Pedido
92
+
93
+ ```bash
94
+ curl -X GET http://localhost:3000/orders/order-1719600600000
95
+ ```
96
+
97
+ **O que acontece:**
98
+ 1. `order-retrieval`: DynamoDB busca o pedido
99
+ 2. CloudBroker rastreia o custo dessa operação
100
+
101
+ ### Verificar Fraude
102
+
103
+ ```bash
104
+ curl -X POST http://localhost:3000/verify-fraud \
105
+ -H "Content-Type: application/json" \
106
+ -d '{
107
+ "orderId": "order-123",
108
+ "customerId": "user-456"
109
+ }'
110
+ ```
111
+
112
+ **O que acontece:**
113
+ 1. `fraud-detection`: DynamoDB consulta perfil do cliente
114
+ 2. Retorna score de fraude (0–1)
115
+
116
+ ### Registrar Evento de Analytics
117
+
118
+ ```bash
119
+ curl -X POST http://localhost:3000/analytics \
120
+ -H "Content-Type: application/json" \
121
+ -d '{
122
+ "userId": "user-789",
123
+ "eventType": "page_view",
124
+ "data": {"page": "/checkout"}
125
+ }'
126
+ ```
127
+
128
+ **O que acontece:**
129
+ 1. `analytics-tracking`: DynamoDB armazena o evento
130
+
131
+ ## Entendendo os Custos
132
+
133
+ Depois de ~5 minutos, no dashboard CloudBroker você verá algo assim:
134
+
135
+ ```
136
+ Feature: order-processing
137
+ Requests: 2
138
+ Total Cost: $0.000003
139
+ Cost per Request: $0.0000015
140
+
141
+ Feature: invoice-generation
142
+ Requests: 2
143
+ Total Cost: $0.000002
144
+ Cost per Request: $0.000001
145
+
146
+ Feature: order-retrieval
147
+ Requests: 1
148
+ Total Cost: $0.00000025
149
+ Cost per Request: $0.00000025
150
+
151
+ Feature: fraud-detection
152
+ Requests: 1
153
+ Total Cost: $0.00000025
154
+ Cost per Request: $0.00000025
155
+
156
+ Feature: analytics-tracking
157
+ Requests: 2
158
+ Total Cost: $0.000003
159
+ Cost per Request: $0.0000015
160
+ ```
161
+
162
+ ## Modo Desenvolvimento
163
+
164
+ Rodar com auto-reload:
165
+
166
+ ```bash
167
+ npm run dev
168
+ ```
169
+
170
+ ## Testes E2E
171
+
172
+ ```bash
173
+ npm test
174
+ ```
175
+
176
+ Runs `test/e2e.js` que faz requisições e valida respostas.
177
+
178
+ ## Troubleshooting
179
+
180
+ ### "Nenhum dado aparecendo no dashboard"
181
+
182
+ 1. ✅ API Key configurada?
183
+ ```bash
184
+ echo $CLOUDBROKER_API_KEY
185
+ ```
186
+
187
+ 2. ✅ AWS credenciais válidas? (tente fazer um `aws s3 ls`)
188
+
189
+ 3. ✅ Esperou 5+ minutos? (CloudBroker faz batch)
190
+
191
+ 4. ✅ Fez pelo menos 1 request pro app?
192
+ ```bash
193
+ curl http://localhost:3000/health
194
+ ```
195
+
196
+ ### "Erro 403 na DynamoDB"
197
+
198
+ - Tabelas `CheckoutOrders`, `CustomerProfiles`, `AnalyticsEvents` não existem na sua conta AWS
199
+ - CloudBroker **não cria tabelas automaticamente** — você precisa criar:
200
+ ```bash
201
+ aws dynamodb create-table \
202
+ --table-name CheckoutOrders \
203
+ --attribute-definitions AttributeName=orderId,AttributeType=S \
204
+ --key-schema AttributeName=orderId,KeyType=HASH \
205
+ --billing-mode PAY_PER_REQUEST
206
+ ```
207
+
208
+ ### "Erro ao conectar no S3"
209
+
210
+ - Bucket `checkout-invoices` não existe
211
+ - Crie:
212
+ ```bash
213
+ aws s3 mb s3://checkout-invoices-YOUR-UID --region us-east-2
214
+ ```
215
+
216
+ ## Próximos Passos
217
+
218
+ - Leia [docs/COST_PER_FEATURE.md](../docs/COST_PER_FEATURE.md) pro guia completo
219
+ - Copie os patterns aqui pro seu próprio backend
220
+ - Cada label representa uma feature — customize conforme suas necessidades
221
+ - Monitore no dashboard CloudBroker qual feature custa mais
222
+
223
+ ## Suporte
224
+
225
+ Dúvidas? Abra issue em [github.com/rmoura151179/cloudbroker-sdk](https://github.com/rmoura151179/cloudbroker-sdk).
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "cloudbroker-sdk-example-checkout",
3
+ "version": "1.0.0",
4
+ "description": "Exemplo: Rastrear custo por feature com CloudBroker SDK",
5
+ "type": "module",
6
+ "main": "server.js",
7
+ "scripts": {
8
+ "start": "node server.js",
9
+ "dev": "node --watch server.js",
10
+ "test": "node test/e2e.js"
11
+ },
12
+ "keywords": ["cloudbroker", "finops", "cost-tracking"],
13
+ "author": "CloudBroker",
14
+ "license": "MIT",
15
+ "dependencies": {
16
+ "express": "^4.18.2",
17
+ "@aws-sdk/client-s3": "^3.400.0",
18
+ "@aws-sdk/client-dynamodb": "^3.400.0",
19
+ "@aws-sdk/lib-dynamodb": "^3.400.0",
20
+ "cloudbroker-sdk": "^0.5.0"
21
+ },
22
+ "devDependencies": {
23
+ "nodemon": "^3.0.1"
24
+ },
25
+ "engines": {
26
+ "node": ">=18.0.0"
27
+ }
28
+ }