funifier-mcp 0.2.26 → 0.2.27
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/.cursor/rules/funifier.mdc +38 -41
- package/.github/copilot-instructions.md +38 -41
- package/AGENTS.md +56 -49
- package/README.md +40 -22
- package/datasource-funifier-docs/.coverage.json +326 -0
- package/datasource-funifier-docs/.validation.json +593 -0
- package/datasource-funifier-docs/knowledge/guides/aggregates.md +182 -70
- package/datasource-funifier-docs/knowledge/guides/database-access.md +174 -88
- package/datasource-funifier-docs/knowledge/guides/java-entities.md +294 -204
- package/datasource-funifier-docs/knowledge/guides/java-libraries.md +202 -226
- package/datasource-funifier-docs/knowledge/guides/java-managers.md +343 -265
- package/datasource-funifier-docs/knowledge/guides/trigger-examples.md +180 -236
- package/datasource-funifier-docs/knowledge/guides/triggers-guide.md +273 -191
- package/datasource-funifier-docs/knowledge/index.md +4 -1
- package/datasource-funifier-docs/knowledge/modules/achievement.md +1126 -28
- package/datasource-funifier-docs/knowledge/modules/action-log.md +469 -62
- package/datasource-funifier-docs/knowledge/modules/action.md +522 -70
- package/datasource-funifier-docs/knowledge/modules/auth.md +718 -69
- package/datasource-funifier-docs/knowledge/modules/avatar.md +483 -18
- package/datasource-funifier-docs/knowledge/modules/backup.md +603 -25
- package/datasource-funifier-docs/knowledge/modules/challenge.md +1048 -220
- package/datasource-funifier-docs/knowledge/modules/compact.md +469 -26
- package/datasource-funifier-docs/knowledge/modules/competition.md +811 -109
- package/datasource-funifier-docs/knowledge/modules/crossword.md +504 -28
- package/datasource-funifier-docs/knowledge/modules/csv-data.md +645 -20
- package/datasource-funifier-docs/knowledge/modules/custom-object.md +701 -36
- package/datasource-funifier-docs/knowledge/modules/database.md +730 -164
- package/datasource-funifier-docs/knowledge/modules/folder.md +935 -280
- package/datasource-funifier-docs/knowledge/modules/kpi-formulas.md +410 -15
- package/datasource-funifier-docs/knowledge/modules/lastmile.md +568 -29
- package/datasource-funifier-docs/knowledge/modules/leaderboard.md +595 -126
- package/datasource-funifier-docs/knowledge/modules/level.md +536 -54
- package/datasource-funifier-docs/knowledge/modules/lottery.md +809 -76
- package/datasource-funifier-docs/knowledge/modules/marketplace.md +688 -17
- package/datasource-funifier-docs/knowledge/modules/mystery.md +662 -52
- package/datasource-funifier-docs/knowledge/modules/notification.md +564 -26
- package/datasource-funifier-docs/knowledge/modules/patterns.md +519 -814
- package/datasource-funifier-docs/knowledge/modules/player.md +773 -73
- package/datasource-funifier-docs/knowledge/modules/point.md +380 -83
- package/datasource-funifier-docs/knowledge/modules/public.md +508 -178
- package/datasource-funifier-docs/knowledge/modules/question.md +619 -99
- package/datasource-funifier-docs/knowledge/modules/quiz.md +565 -120
- package/datasource-funifier-docs/knowledge/modules/scheduler.md +1092 -39
- package/datasource-funifier-docs/knowledge/modules/security.md +674 -112
- package/datasource-funifier-docs/knowledge/modules/staging.md +742 -19
- package/datasource-funifier-docs/knowledge/modules/story.md +565 -29
- package/datasource-funifier-docs/knowledge/modules/studio-page.md +470 -144
- package/datasource-funifier-docs/knowledge/modules/swap.md +552 -84
- package/datasource-funifier-docs/knowledge/modules/team.md +563 -45
- package/datasource-funifier-docs/knowledge/modules/trigger.md +876 -134
- package/datasource-funifier-docs/knowledge/modules/upload.md +468 -95
- package/datasource-funifier-docs/knowledge/modules/virtual-good.md +510 -63
- package/datasource-funifier-docs/knowledge/modules/webhook.md +375 -28
- package/datasource-funifier-docs/knowledge/modules/websocket.md +459 -26
- package/datasource-funifier-docs/knowledge/modules/widget.md +613 -27
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +42 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/init.test.js +74 -3
- package/dist/cli/init.test.js.map +1 -1
- package/dist/cli/persona.d.ts +3 -0
- package/dist/cli/persona.d.ts.map +1 -0
- package/dist/cli/persona.js +25 -0
- package/dist/cli/persona.js.map +1 -0
- package/dist/mcp/bundle.js +119 -93
- package/dist/mcp/index.js +2 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/resources/documentation.d.ts +1 -1
- package/dist/mcp/resources/documentation.d.ts.map +1 -1
- package/dist/mcp/resources/documentation.js +39 -3
- package/dist/mcp/resources/documentation.js.map +1 -1
- package/dist/mcp/tools/connect.d.ts.map +1 -1
- package/dist/mcp/tools/connect.js +18 -8
- package/dist/mcp/tools/connect.js.map +1 -1
- package/dist/mcp/tools/database.d.ts.map +1 -1
- package/dist/mcp/tools/database.js +59 -47
- package/dist/mcp/tools/database.js.map +1 -1
- package/dist/mcp/tools/database.test.js +2 -2
- package/dist/mcp/tools/database.test.js.map +1 -1
- package/dist/mcp/tools/delete.d.ts.map +1 -1
- package/dist/mcp/tools/delete.js +13 -3
- package/dist/mcp/tools/delete.js.map +1 -1
- package/dist/mcp/tools/execute.d.ts.map +1 -1
- package/dist/mcp/tools/execute.js +20 -9
- package/dist/mcp/tools/execute.js.map +1 -1
- package/dist/mcp/tools/folder.d.ts.map +1 -1
- package/dist/mcp/tools/folder.js +22 -12
- package/dist/mcp/tools/folder.js.map +1 -1
- package/dist/mcp/tools/get.d.ts.map +1 -1
- package/dist/mcp/tools/get.js +16 -6
- package/dist/mcp/tools/get.js.map +1 -1
- package/dist/mcp/tools/index.d.ts +1 -1
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/index.js +3 -1
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/list.d.ts.map +1 -1
- package/dist/mcp/tools/list.js +38 -14
- package/dist/mcp/tools/list.js.map +1 -1
- package/dist/mcp/tools/logs.d.ts.map +1 -1
- package/dist/mcp/tools/logs.js +15 -5
- package/dist/mcp/tools/logs.js.map +1 -1
- package/dist/mcp/tools/save.d.ts.map +1 -1
- package/dist/mcp/tools/save.js +14 -4
- package/dist/mcp/tools/save.js.map +1 -1
- package/dist/mcp/tools/save.test.js +3 -3
- package/dist/mcp/tools/save.test.js.map +1 -1
- package/dist/mcp/tools/search-docs.d.ts +3 -0
- package/dist/mcp/tools/search-docs.d.ts.map +1 -0
- package/dist/mcp/tools/search-docs.js +102 -0
- package/dist/mcp/tools/search-docs.js.map +1 -0
- package/package.json +6 -2
- package/skills/acquire-funifier-knowledge/SKILL.md +132 -0
- package/skills/acquire-funifier-knowledge/assets/templates/CONCERNS.md +25 -0
- package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_ENDPOINTS.md +24 -0
- package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_PAGES.md +24 -0
- package/skills/acquire-funifier-knowledge/assets/templates/GAME_MECHANICS.md +35 -0
- package/skills/acquire-funifier-knowledge/assets/templates/INTEGRATIONS.md +35 -0
- package/skills/acquire-funifier-knowledge/assets/templates/LEADERBOARDS.md +24 -0
- package/skills/acquire-funifier-knowledge/assets/templates/OVERVIEW.md +47 -0
- package/skills/acquire-funifier-knowledge/assets/templates/PLAYER_MODEL.md +31 -0
- package/skills/acquire-funifier-knowledge/assets/templates/SCHEDULERS.md +25 -0
- package/skills/acquire-funifier-knowledge/assets/templates/TECHNIQUES_AND_PATTERNS.md +26 -0
- package/skills/acquire-funifier-knowledge/assets/templates/TRIGGERS.md +27 -0
- package/skills/acquire-funifier-knowledge/references/funifier-inventory-checklist.md +81 -0
- package/skills/acquire-funifier-knowledge/references/game-techniques-taxonomy.md +62 -0
- package/skills/acquire-funifier-knowledge/references/mcp-call-patterns.md +118 -0
- package/skills/funifier/SKILL.md +88 -0
- package/skills/funifier/references/configure-security.md +96 -0
- package/skills/{funifier-create-action/SKILL.md → funifier/references/create-action.md} +0 -33
- package/skills/funifier/references/create-aggregate.md +144 -0
- package/skills/funifier/references/create-challenge.md +116 -0
- package/skills/funifier/references/create-competition.md +98 -0
- package/skills/funifier/references/create-crossword.md +574 -0
- package/skills/funifier/references/create-custom-object.md +91 -0
- package/skills/funifier/references/create-custom-page.md +135 -0
- package/skills/funifier/references/create-folder.md +104 -0
- package/skills/funifier/references/create-lastmile.md +643 -0
- package/skills/{funifier-create-leaderboard/SKILL.md → funifier/references/create-leaderboard.md} +0 -33
- package/skills/funifier/references/create-level.md +94 -0
- package/skills/funifier/references/create-lottery.md +913 -0
- package/skills/funifier/references/create-mystery.md +769 -0
- package/skills/funifier/references/create-notification.md +75 -0
- package/skills/{funifier-create-point/SKILL.md → funifier/references/create-point.md} +0 -33
- package/skills/funifier/references/create-quiz.md +98 -0
- package/skills/funifier/references/create-scheduler.md +141 -0
- package/skills/funifier/references/create-story.md +636 -0
- package/skills/funifier/references/create-swap.md +95 -0
- package/skills/{funifier-create-trigger/SKILL.md → funifier/references/create-trigger.md} +0 -33
- package/skills/funifier/references/create-virtual-good.md +96 -0
- package/skills/funifier/references/create-webhook.md +72 -0
- package/skills/funifier/references/create-websocket.md +71 -0
- package/skills/funifier/references/create-widget.md +76 -0
- package/skills/funifier/references/debug.md +87 -0
- package/skills/funifier/references/help.md +81 -0
- package/skills/funifier/references/implement-frontend.md +106 -0
- package/skills/funifier/references/import-csv.md +75 -0
- package/skills/funifier/references/manage-player.md +82 -0
- package/skills/funifier/references/manage-team.md +76 -0
- package/skills/funifier/references/upload-file.md +91 -0
- package/skills/funifier-create-aggregate/SKILL.md +0 -127
- package/skills/funifier-create-challenge/SKILL.md +0 -88
- package/skills/funifier-create-custom-page/SKILL.md +0 -127
- package/skills/funifier-create-level/SKILL.md +0 -87
- package/skills/funifier-create-quiz/SKILL.md +0 -87
- package/skills/funifier-create-scheduler/SKILL.md +0 -127
- package/skills/funifier-create-virtual-good/SKILL.md +0 -87
- package/skills/funifier-debug/SKILL.md +0 -92
- package/skills/funifier-help/SKILL.md +0 -86
- package/skills/funifier-implement-frontend/SKILL.md +0 -90
- package/skills/funifier-index/SKILL.md +0 -58
|
@@ -1,40 +1,483 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `compact`
|
|
2
2
|
|
|
3
|
-
**Acesso Studio:** `/studio/compact`
|
|
4
3
|
**API Endpoint:** `/v3/compact`
|
|
4
|
+
**Coleção MongoDB:** `compact` (configurações) e `compact_log` (logs de execução)
|
|
5
|
+
**Acesso Studio:** não verificado neste repositório — o módulo foi confirmado apenas via API (`funifier-service` não contém código de UID/Studio para `compact`).
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
> Documentação de engenharia reversa baseada exclusivamente no código-fonte de `funifier-service`
|
|
8
|
+
> (`src/main/java/com/funifier/engine/compact/` e `src/main/java/com/funifier/rest/v3/rest/CompactRest.java`).
|
|
9
|
+
> Reflete o comportamento **real** do runtime, não o comportamento REST convencional.
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
---
|
|
9
12
|
|
|
10
|
-
##
|
|
13
|
+
## 1. Visão Geral
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
- Para otimizar coleções de acesso rápido
|
|
14
|
-
- Para reduzir volume de dados históricos
|
|
15
|
+
O módulo `compact` executa **compactação de coleções MongoDB via pipeline de agregação definido pelo cliente**. Ele não faz "compressão de armazenamento" genérica — ele **substitui N documentos por M documentos agregados** (M ≤ N) dentro de uma mesma coleção.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
O caso de uso canônico (visto em `_CompactTest`) é compactar o histórico de `achievement`: milhares de registros individuais antigos de um jogador são agrupados (`$group`) em um único registro consolidado com `total` somado e a lista dos `_id` originais preservada em `extra.compact`.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
- [ ] Configurar critérios de agrupamento
|
|
20
|
-
- [ ] Testar impacto na performance
|
|
19
|
+
Papel arquitetural:
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
- É uma ferramenta **administrativa/operacional de manutenção de dados**, não um recurso de gamificação voltado ao jogador final.
|
|
22
|
+
- Opera sobre **qualquer coleção** do banco do tenant — a coleção-alvo é um parâmetro (`collection`), não fixa.
|
|
23
|
+
- O `match` (filtro) e o `aggregate` (pipeline) são **comandos MongoDB crus**, fornecidos pelo cliente e executados diretamente. O módulo é essencialmente um executor genérico de "agregação destrutiva".
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
**Método:** GET
|
|
26
|
-
**Endpoint:** `/v3/compact`
|
|
25
|
+
Componentes (`com.funifier.engine.compact`):
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
| Classe | Papel |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `Compact` | Entidade de configuração (coleção `compact`). |
|
|
30
|
+
| `CompactLog` (+ `CompactLog.Total`) | Log/estado de uma execução (coleção `compact_log`). |
|
|
31
|
+
| `CompactManager` | Manager: `save`, `delete`, `find`, `execute`, `compact`, `asyncExecute`, `findLog`. |
|
|
32
|
+
| `CompactAsyncProcessor` | `Runnable` com fila em memória; uma thread por tenant. |
|
|
33
|
+
| `CompactRest` | Resource JAX-RS em `/v3/compact`. |
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
**Método:** DELETE
|
|
34
|
-
**Endpoint:** `/v3/compact/:id`
|
|
35
|
+
O `CompactManager` é instanciado por tenant em `ManagerFactory` (linha 130), e seu construtor inicia imediatamente a thread assíncrona `Funifier-Compact-Async-<apiKey>`.
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
---
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
## 2. Arquitetura e Fluxos
|
|
40
|
+
|
|
41
|
+
### 2.1 Pipeline principal
|
|
42
|
+
|
|
43
|
+
A operação é dividida em uma **fase síncrona** (disparo) e uma **fase assíncrona** (execução real em background).
|
|
44
|
+
|
|
45
|
+
**Fase síncrona — `CompactManager.execute(id)` (chamada por `CompactRest.execute`):**
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
1. busca Compact em "compact" por _id
|
|
49
|
+
se não existir → retorna null (nada acontece)
|
|
50
|
+
2. busca em "compact_log" um log com {compact:id, finish:{$exists:false}} (execução em andamento)
|
|
51
|
+
se existir → retorna esse log e NÃO inicia nova execução (guard de concorrência)
|
|
52
|
+
se não existir → segue para 3
|
|
53
|
+
3. cria CompactLog {status:"started", total:{original:0, compacted:0}, start:now}
|
|
54
|
+
salva em "compact_log"
|
|
55
|
+
async.track(log) → enfileira para a thread assíncrona
|
|
56
|
+
4. retorna o log imediatamente (status "started")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`execute` é `synchronized`. O método `compact(id)` também existe, é `synchronized` e apenas delega para `execute(id)` — **não é usado pelo REST** (entrada alternativa/legada).
|
|
60
|
+
|
|
61
|
+
**Fase assíncrona — `CompactManager.asyncExecute(log)` (rodando na thread do tenant):**
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
5. re-busca Compact por log.compact → se null, encerra em silêncio
|
|
65
|
+
6. source = coleção(compact.collection)
|
|
66
|
+
7. total.original = source.count(match) ; status="count items" ; salva log
|
|
67
|
+
8. parse do aggregate (JSON array) em estágios
|
|
68
|
+
monta source.aggregate("{$match:...}") + estágios, allowDiskUse(true) ; status="group data" ; salva
|
|
69
|
+
9. tmp = coleção "<logId>_tmp" ; tmp.drop()
|
|
70
|
+
para cada doc agregado: tmp.save(doc) ; status="temp saved : N" (a cada 50)
|
|
71
|
+
10. source.remove(match) ; status="removing original" ; salva ← REMOVE ORIGINAIS
|
|
72
|
+
11. para cada doc em tmp: source.save(doc) ; status="compacted saved : N" (a cada 50)
|
|
73
|
+
12. tmp.drop()
|
|
74
|
+
13. total.compacted=N ; status="finished" ; finish=now ; salva
|
|
75
|
+
14. dispara trigger after_finish na entidade "compact_log"
|
|
76
|
+
em qualquer exceção: status="error" ; finish=now ; message=<erro> ; salva ; (NENHUM trigger)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Características confirmadas no código:
|
|
80
|
+
|
|
81
|
+
- **Assíncrono e serial por tenant.** A fila (`LinkedBlockingQueue` em `CompactAsyncProcessor`) é consumida por uma única thread por tenant. Dois `execute` para `compact` **diferentes** no mesmo tenant **enfileiram e rodam em sequência, não em paralelo**.
|
|
82
|
+
- **Sem transação.** O `remove` da origem (passo 10) acontece **antes** da reinserção (passo 11). Não há rollback.
|
|
83
|
+
- **`allowDiskUse(true)`** está habilitado na agregação (suporta datasets grandes).
|
|
84
|
+
- **Trigger só no sucesso.** O `after_finish` (passo 14) está no caminho feliz; em erro, o `catch` registra o log e não dispara trigger.
|
|
85
|
+
|
|
86
|
+
### 2.2 Fluxo de execução assíncrona — `asyncExecute`
|
|
87
|
+
|
|
88
|
+
```mermaid
|
|
89
|
+
flowchart TD
|
|
90
|
+
A["execute(id) cria CompactLog<br/>status: started"] --> B["async.track(log)<br/>enfileira"]
|
|
91
|
+
B --> C{"thread do tenant<br/>poll na fila"}
|
|
92
|
+
C --> D["asyncExecute(log)"]
|
|
93
|
+
D --> E{"Compact ainda<br/>existe?"}
|
|
94
|
+
E -- não --> Z1["encerra em silêncio"]
|
|
95
|
+
E -- sim --> F["count(match)<br/>total.original"]
|
|
96
|
+
F --> G["agrega para tmp<br/><logId>_tmp"]
|
|
97
|
+
G --> H["source.remove(match)<br/>REMOVE ORIGINAIS"]
|
|
98
|
+
H --> I["reinsere tmp em source<br/>total.compacted"]
|
|
99
|
+
I --> J["tmp.drop()"]
|
|
100
|
+
J --> K["status: finished<br/>finish: now"]
|
|
101
|
+
K --> L["trigger after_finish<br/>(entidade compact_log)"]
|
|
102
|
+
F -.->|exceção| X["status: error<br/>message, finish<br/>(sem trigger)"]
|
|
103
|
+
G -.->|exceção| X
|
|
104
|
+
H -.->|exceção| X
|
|
105
|
+
I -.->|exceção| X
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2.3 Interação entre componentes
|
|
109
|
+
|
|
110
|
+
```mermaid
|
|
111
|
+
sequenceDiagram
|
|
112
|
+
participant C as Cliente
|
|
113
|
+
participant R as CompactRest
|
|
114
|
+
participant M as CompactManager
|
|
115
|
+
participant Q as CompactAsyncProcessor (thread/tenant)
|
|
116
|
+
participant DB as MongoDB
|
|
117
|
+
participant T as TriggerManager
|
|
118
|
+
|
|
119
|
+
C->>R: GET /v3/compact/{id}/execute
|
|
120
|
+
R->>M: execute(id)
|
|
121
|
+
M->>DB: findOne compact / log em andamento
|
|
122
|
+
alt log em andamento existe
|
|
123
|
+
M-->>R: log existente (sem nova execução)
|
|
124
|
+
else
|
|
125
|
+
M->>DB: save(CompactLog status=started)
|
|
126
|
+
M->>Q: track(log)
|
|
127
|
+
M-->>R: log (started)
|
|
128
|
+
end
|
|
129
|
+
R-->>C: 200 + CompactLog
|
|
130
|
+
|
|
131
|
+
Note over Q: assíncrono
|
|
132
|
+
Q->>M: asyncExecute(log)
|
|
133
|
+
M->>DB: count(match) / aggregate -> tmp
|
|
134
|
+
M->>DB: remove(match) / reinsere tmp / drop tmp
|
|
135
|
+
M->>DB: save(log finished)
|
|
136
|
+
M->>T: execute(after_finish, compact_log)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 3. Estrutura dos Objetos
|
|
142
|
+
|
|
143
|
+
### 3.1 `Compact` — documento de configuração (coleção `compact`)
|
|
144
|
+
|
|
145
|
+
| Campo | Tipo | Padrão | Obrigatório | Descrição |
|
|
146
|
+
| --- | --- | --- | --- | --- |
|
|
147
|
+
| `_id` | String | auto (`Guid.shortTimeMillis()`) | — | Gerado se ausente/vazio no `save`. Cliente pode fornecer o próprio. |
|
|
148
|
+
| `title` | String | — | Não | Rótulo descritivo. **Não é validado nem usado em nenhuma lógica.** |
|
|
149
|
+
| `collection` | String | — | **Sim** | Coleção MongoDB de origem a ser compactada. |
|
|
150
|
+
| `match` | String | — | **Sim** | Filtro MongoDB (JSON) que seleciona os documentos a compactar. Suporta keywords de data Funifier (ver 3.4). |
|
|
151
|
+
| `aggregate` | String | — | **Sim** | Pipeline de agregação MongoDB (JSON **array**) que produz os documentos compactados. |
|
|
152
|
+
| `created` | Date | auto | — | Definido no primeiro `save` (quando `_id` é gerado). |
|
|
153
|
+
| `updated` | Date | auto | — | Atualizado em **todo** `save`. |
|
|
154
|
+
|
|
155
|
+
**Tipo dos campos `match` e `aggregate`:** são `String` em Java. O corpo da requisição deve enviá-los como **strings JSON escapadas** (JSON-encoded), **não** como objeto/array JSON nativo. Não foi encontrado `ContextResolver<ObjectMapper>` customizado no JAX-RS, então a desserialização padrão do Jackson exige string para um campo `String`. Todos os exemplos da seção 10 seguem esse contrato.
|
|
156
|
+
|
|
157
|
+
**Campos legados/comentados (NÃO suportados):**
|
|
158
|
+
|
|
159
|
+
- `active` (boolean) — comentado em `Compact.java`. **Não há flag de ativação/desativação.**
|
|
160
|
+
- `cron` (String) — comentado. **Não há agendamento automático.** Toda execução é manual via endpoint.
|
|
161
|
+
|
|
162
|
+
> O comentário de design no topo de `CompactManager.java` menciona `{title, active, collection, match, group, cron}`, mas `active`, `group` e `cron` não existem no runtime — `aggregate` substituiu `group`.
|
|
163
|
+
|
|
164
|
+
### 3.2 `CompactLog` — log/estado de execução (coleção `compact_log`)
|
|
165
|
+
|
|
166
|
+
| Campo | Tipo | Descrição |
|
|
167
|
+
| --- | --- | --- |
|
|
168
|
+
| `_id` | String | `Guid.shortTimeMillis()`. **Também é o nome base da coleção temporária** `<_id>_tmp`. |
|
|
169
|
+
| `compact` | String | `_id` da configuração `Compact` associada. |
|
|
170
|
+
| `status` | String | Estado textual do processo (ver 3.3). Não é enum — é string livre. |
|
|
171
|
+
| `start` | Date | Início da execução. |
|
|
172
|
+
| `finish` | Date | Fim da execução. **Ausente enquanto em andamento** — campo-chave do guard de concorrência. |
|
|
173
|
+
| `total` | `Total` | Contagens de itens (ver abaixo). |
|
|
174
|
+
| `message` | String | Mensagem de erro (`e.getMessage()`), preenchida apenas em falha. |
|
|
175
|
+
|
|
176
|
+
`CompactLog.Total`:
|
|
177
|
+
|
|
178
|
+
| Campo | Tipo | Descrição |
|
|
179
|
+
| --- | --- | --- |
|
|
180
|
+
| `original` | long | Quantidade de documentos que casaram com `match` (antes da compactação). |
|
|
181
|
+
| `compacted` | long | Quantidade de documentos reinseridos após a agregação. |
|
|
182
|
+
|
|
183
|
+
### 3.3 Ciclo de vida de `CompactLog.status`
|
|
184
|
+
|
|
185
|
+
```mermaid
|
|
186
|
+
stateDiagram-v2
|
|
187
|
+
[*] --> started: execute()
|
|
188
|
+
started --> count_items: count(match)
|
|
189
|
+
count_items --> group_data: monta aggregate
|
|
190
|
+
group_data --> temp_saved: insere em tmp (a cada 50)
|
|
191
|
+
temp_saved --> removing_original: source.remove(match)
|
|
192
|
+
removing_original --> compacted_saved: reinsere (a cada 50)
|
|
193
|
+
compacted_saved --> finished: finish=now + trigger
|
|
194
|
+
finished --> [*]
|
|
195
|
+
count_items --> error: exceção
|
|
196
|
+
group_data --> error: exceção
|
|
197
|
+
temp_saved --> error: exceção
|
|
198
|
+
removing_original --> error: exceção
|
|
199
|
+
compacted_saved --> error: exceção
|
|
200
|
+
error --> [*]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Valores reais de `status`: `started`, `count items`, `group data`, `temp saved : <N>`, `removing original`, `compacted saved : <N>`, `finished`, `error`.
|
|
204
|
+
|
|
205
|
+
### 3.4 Keywords de data no `match` (`FunifierMarshaller`)
|
|
206
|
+
|
|
207
|
+
Antes de executar, `match` passa por `FunifierMarshaller.toString`, que converte expressões de data Funifier em epoch millis. Ex. (de `_CompactTest`):
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{"time":{ "$gte":{"$date":"-10y"}, "$lte":{"$date":"now"} }}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`{"$date":"-10y"}` e `{"$date":"now"}` são resolvidos para timestamps numéricos via `DateUtil.fromKeyword`. Isso permite filtros relativos (ex.: "tudo com mais de 10 anos").
|
|
214
|
+
|
|
215
|
+
> Observação técnica: no passo do `$match` da agregação o código aplica `FunifierMarshaller.toString` **duas vezes** (`toString(toString(match))`). A segunda passagem é redundante (re-parse/re-serialize de um JSON já normalizado) — inofensiva, mas é código a ser simplificado.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 4. Endpoints
|
|
220
|
+
|
|
221
|
+
Base: `/v3/compact` (`CompactRest`). Autenticação via `AuthBean` (Bearer token JAX-RS); o nível de permissão exato não foi verificado nesta leitura.
|
|
222
|
+
|
|
223
|
+
### `POST /v3/compact` — criar/atualizar configuração
|
|
224
|
+
|
|
225
|
+
| Aspecto | Detalhe |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| Finalidade | Persiste uma `Compact` (cria se `_id` ausente, atualiza se presente). |
|
|
228
|
+
| Handler | `CompactRest.insert` → `CompactManager.save` |
|
|
229
|
+
| Full replace ou patch | **Full replace** — `col.save(obj)` substitui o documento inteiro. |
|
|
230
|
+
| Status de sucesso | `201 CREATED` |
|
|
231
|
+
| Resposta | Objeto `Compact` com nulos removidos (`toJsonRemoveNullFields`). |
|
|
232
|
+
|
|
233
|
+
**Comportamento real (crítico):** `save` só persiste se `obj != null && collection != null && match != null && aggregate != null`. Se qualquer um (`collection`, `match`, `aggregate`) for nulo, **nada é gravado e nenhum erro é retornado** — o endpoint ainda responde `201 CREATED` com o corpo enviado. `title` **não** é obrigatório. Ver seções 5 e Checklist.
|
|
234
|
+
|
|
235
|
+
### `GET /v3/compact/{id}` — buscar configuração
|
|
236
|
+
|
|
237
|
+
| Aspecto | Detalhe |
|
|
238
|
+
| --- | --- |
|
|
239
|
+
| Finalidade | Retorna a `Compact` por `_id`. |
|
|
240
|
+
| Handler | `CompactRest.find` → `CompactManager.find` |
|
|
241
|
+
| Status | `200 OK` |
|
|
242
|
+
| Resposta | `Compact` (nulos removidos). Se `_id` não existe, corpo vazio/`null`. |
|
|
243
|
+
|
|
244
|
+
### `DELETE /v3/compact/{id}` — remover configuração
|
|
245
|
+
|
|
246
|
+
| Aspecto | Detalhe |
|
|
247
|
+
| --- | --- |
|
|
248
|
+
| Finalidade | Remove a `Compact` **e todos os seus logs** associados. |
|
|
249
|
+
| Handler | `CompactRest.delete` → `CompactManager.delete` |
|
|
250
|
+
| Status | `204 NO CONTENT` |
|
|
251
|
+
|
|
252
|
+
**Comportamento real:** cascata — remove de `compact` por `_id` **e** de `compact_log` por `{compact:id}`. Não remove dados já compactados na coleção de origem (a compactação é irreversível por aqui).
|
|
253
|
+
|
|
254
|
+
### `GET /v3/compact/{id}/execute` — disparar compactação
|
|
255
|
+
|
|
256
|
+
| Aspecto | Detalhe |
|
|
257
|
+
| --- | --- |
|
|
258
|
+
| Finalidade | Inicia (ou retorna a execução em andamento) da compactação. |
|
|
259
|
+
| Handler | `CompactRest.execute` → `CompactManager.execute` |
|
|
260
|
+
| Verbo | **`GET`** — apesar de causar efeito colateral destrutivo (não é idempotente no sentido REST). |
|
|
261
|
+
| Status | `200 OK` |
|
|
262
|
+
| Resposta | `CompactLog` recém-criado (`started`) ou o log em andamento existente. |
|
|
263
|
+
|
|
264
|
+
**Comportamento real:** retorna **imediatamente** (processamento assíncrono). Se já houver um log sem `finish` para esse `compact`, retorna esse log e **não** inicia nova execução. Use o `_id` do log retornado para acompanhar o progresso consultando a coleção `compact_log`.
|
|
265
|
+
|
|
266
|
+
**Exemplo de resposta de `execute`:**
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"_id": "Xa9bC2",
|
|
271
|
+
"compact": "c1",
|
|
272
|
+
"status": "started",
|
|
273
|
+
"start": 1747699200000,
|
|
274
|
+
"total": { "original": 0, "compacted": 0 }
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 5. Regras de Negócio
|
|
281
|
+
|
|
282
|
+
Regras presentes no código mas ausentes do schema:
|
|
283
|
+
|
|
284
|
+
- **Validação silenciosa no `save`.** Sem `collection`/`match`/`aggregate`, o `save` é ignorado sem erro — porém o `POST` retorna `201`. É o footgun mais perigoso do módulo.
|
|
285
|
+
- **Guard de concorrência por log não finalizado.** `execute` não inicia nova execução enquanto existir um `compact_log` sem `finish` para o mesmo `compact`. Isso evita execuções concorrentes — mas também **trava permanentemente** novas execuções se um log ficar "preso" (ver seção 9).
|
|
286
|
+
- **Execução serial por tenant.** Uma única thread por tenant processa a fila; compactações concorrem em fila, não em paralelo.
|
|
287
|
+
- **Janela de perda de dados (não-transacional).** O passo `source.remove(match)` ocorre **antes** da reinserção. Uma falha do servidor entre remover e reinserir deixa os originais apenas na coleção temporária `<logId>_tmp` (que **não** é dropada em caso de crash). Não há rollback automático.
|
|
288
|
+
- **`aggregate` deve produzir substitutos válidos.** O cliente é responsável por escrever um pipeline que gere documentos que substituam corretamente os originais. Convenção observada em `_CompactTest`: `$group` + `$project` preservando os `_id` originais em `extra.compact` e gerando novo `_id`. Nada disso é validado pelo código.
|
|
289
|
+
- **Comandos MongoDB crus.** `match` e `aggregate` são executados diretamente contra o MongoDB. Qualquer coleção do tenant pode ser alvo. Ver seção 8.
|
|
290
|
+
- **Multi-tenant.** Toda operação ocorre via `manager.getJongoConnection()`, que é a conexão do banco do tenant (resolvido pela `apiKey`). Isolamento é por banco de tenant.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 6. Comportamentos Automáticos
|
|
295
|
+
|
|
296
|
+
| Comportamento | Trigger | Impacto | Persistência |
|
|
297
|
+
| --- | --- | --- | --- |
|
|
298
|
+
| Geração de `_id` | `save` com `_id` nulo/vazio | `Guid.shortTimeMillis()` + `created=now` | coleção `compact` |
|
|
299
|
+
| `updated` automático | todo `save` | `updated=now` | coleção `compact` |
|
|
300
|
+
| Cascata de logs | `DELETE /{id}` | remove todos `compact_log` do compact | coleção `compact_log` |
|
|
301
|
+
| Atualização de status | durante `asyncExecute` | salva log a cada estágio e a cada 50 inserções | coleção `compact_log` |
|
|
302
|
+
| Trigger `after_finish` | fim **bem-sucedido** de `asyncExecute` | dispara `TriggerManager.execute` | conforme triggers configurados |
|
|
303
|
+
|
|
304
|
+
**Trigger `after_finish`** — única integração externa do módulo. Chamada (passo 14):
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
TriggerManager.execute(
|
|
308
|
+
id = log.compact, // _id da config Compact
|
|
309
|
+
o = log, // o CompactLog finalizado
|
|
310
|
+
entity = "compact_log",
|
|
311
|
+
event = "after_finish",
|
|
312
|
+
player = null,
|
|
313
|
+
context = { extra: { "compact": <Compact> } }
|
|
314
|
+
)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Para reagir ao término de uma compactação, registre um trigger na entidade `compact_log` com evento `after_finish`. **Não dispara em caso de erro.**
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## 7. Suportado vs NÃO Suportado
|
|
322
|
+
|
|
323
|
+
### ✅ Suportado
|
|
324
|
+
|
|
325
|
+
- Criar/atualizar configuração de compactação (`POST`).
|
|
326
|
+
- Buscar configuração por `_id` (`GET /{id}`).
|
|
327
|
+
- Deletar configuração + cascata de logs (`DELETE /{id}`).
|
|
328
|
+
- Disparar compactação manualmente (`GET /{id}/execute`).
|
|
329
|
+
- Pipeline de agregação MongoDB arbitrário definido pelo cliente.
|
|
330
|
+
- Filtro `match` com keywords de data relativas (`-10y`, `now`, etc.).
|
|
331
|
+
- Acompanhamento de progresso via documento `compact_log` (status textual + totais).
|
|
332
|
+
- Trigger `after_finish` no término bem-sucedido.
|
|
333
|
+
- `allowDiskUse` em agregações grandes.
|
|
334
|
+
|
|
335
|
+
### ❌ NÃO Suportado
|
|
336
|
+
|
|
337
|
+
- **Listar configurações** — não existe `GET /v3/compact` (sem endpoint de listagem). A documentação antiga que mencionava "Listar Compactações" estava incorreta.
|
|
338
|
+
- **Ler `CompactLog` via REST** — `findLog` existe no manager mas **não é exposto**. O acompanhamento depende de consultar a coleção `compact_log` por outra via (ex.: API de database/aggregate).
|
|
339
|
+
- **Agendamento/cron** — campo `cron` comentado; nenhuma execução automática.
|
|
340
|
+
- **Ativar/desativar** — campo `active` comentado; toda config é sempre "executável".
|
|
341
|
+
- **Transacionalidade / rollback** — não há; existe janela de perda de dados (seção 5).
|
|
342
|
+
- **Reversão de compactação** — o `DELETE` remove config e logs, não restaura documentos compactados.
|
|
343
|
+
- **Durabilidade da fila** — a fila é em memória (`LinkedBlockingQueue`); reinício do servidor perde execuções enfileiradas e pode deixar logs presos.
|
|
344
|
+
- **Recuperação automática de log preso** — não há; intervenção manual em `compact_log` é necessária.
|
|
345
|
+
- **Validação do pipeline** — `aggregate`/`match` não são validados; erros só aparecem em runtime no `compact_log` (status `error`).
|
|
346
|
+
- **`title` operacional** — aceito mas ignorado pela lógica.
|
|
347
|
+
- **Trigger em falha** — `after_finish` não dispara em erro; não há evento de erro.
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 8. Segurança e Permissões
|
|
352
|
+
|
|
353
|
+
- **Autenticação:** via `AuthBean` (Bearer token, padrão JAX-RS dos resources `/v3`). O nível/role exigido **não foi verificado** nesta leitura — não confirme "somente admin" sem checar o filtro de autenticação.
|
|
354
|
+
- **Isolamento por tenant:** todas as operações usam a conexão Jongo do tenant (resolvida pela `apiKey`), confinando o efeito ao banco daquele tenant.
|
|
355
|
+
- **Superfície de injeção / capacidade perigosa (confirmada no código):** `match` e `aggregate` são **comandos MongoDB crus** fornecidos pelo cliente e executados diretamente (`source.count`, `source.aggregate`, `source.remove`). Quem puder chamar `/v3/compact`:
|
|
356
|
+
- pode apontar `collection` para **qualquer coleção** do tenant (inclusive coleções de sistema/configuração);
|
|
357
|
+
- pode escrever um `match` que casa com **todos** os documentos e um `aggregate` que os reduz/destrói — efetivamente um `remove` em massa disfarçado de compactação;
|
|
358
|
+
- não há lista de coleções permitidas, nem sanitização do pipeline.
|
|
359
|
+
Trate o acesso a este módulo como **privilégio administrativo de banco de dados**, não como recurso de aplicação.
|
|
360
|
+
- **`GET` com efeito destrutivo:** `GET /{id}/execute` modifica dados. Isso pode ser disparado acidentalmente por mecanismos que assumem `GET` como seguro (prefetch, crawlers, retries). Considere isso ao expor a rota.
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 9. Observabilidade e Troubleshooting
|
|
365
|
+
|
|
366
|
+
Não há endpoint REST para ler logs; investigue diretamente na coleção `compact_log`.
|
|
367
|
+
|
|
368
|
+
**Verificar a execução mais recente de um compact:**
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
// na coleção compact_log
|
|
372
|
+
{ "compact": "<compactId>" } // ordenar por start desc
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Encontrar execuções presas (em andamento, sem `finish`):**
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
{ "finish": { "$exists": false } }
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
- Erros comuns e causas:
|
|
382
|
+
- **`POST` retornou 201 mas a config "sumiu":** faltou `collection`, `match` ou `aggregate` → `save` ignorado silenciosamente. Confira com `GET /v3/compact/{id}`.
|
|
383
|
+
- **`execute` sempre retorna o mesmo log `started`/intermediário e nunca roda de novo:** existe um `compact_log` sem `finish` (log preso, geralmente por reinício do servidor durante o processamento). O guard em `execute` bloqueia novas execuções. **Correção:** localizar o log preso (`{compact:id, finish:{$exists:false}}`) e removê-lo ou definir `finish`; verificar se a coleção temporária `<logId>_tmp` contém dados ainda não reinseridos antes de descartá-la.
|
|
384
|
+
- **`status: "error"`:** ler o campo `message` do `compact_log` (vem de `e.getMessage()`); causas típicas: `aggregate` malformado, `match` inválido, `collection` inexistente.
|
|
385
|
+
- **Dados originais sumiram e não foram recompactados:** possível crash entre `remove` (passo 10) e reinserção (passo 11). Procurar a coleção `<logId>_tmp` — ela retém os documentos agregados e **não** é dropada em crash.
|
|
386
|
+
|
|
387
|
+
**Diagnóstico de saúde do módulo:** a thread `Funifier-Compact-Async-<apiKey>` deve estar viva por tenant; ela loga `COMECOU EXECUCAO DA THREAD COMPACT ASYNC` no start. Sem ela, logs ficam em `started` para sempre.
|
|
388
|
+
|
|
389
|
+
Comandos úteis:
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
GET /v3/compact/<id> # ver a configuração
|
|
393
|
+
GET /v3/compact/<id>/execute # disparar e obter o _id do log
|
|
394
|
+
# acompanhar progresso: consultar compact_log por _id do log (status/total)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## 10. Exemplos Práticos
|
|
400
|
+
|
|
401
|
+
> Lembre-se: `match` e `aggregate` vão no corpo como **strings JSON escapadas**.
|
|
402
|
+
|
|
403
|
+
### 10.1 Exemplo mínimo funcional
|
|
404
|
+
|
|
405
|
+
Compactar achievements antigos de cada jogador em um único registro consolidado (baseado em `_CompactTest`):
|
|
406
|
+
|
|
407
|
+
```json
|
|
408
|
+
POST /v3/compact
|
|
409
|
+
{
|
|
410
|
+
"_id": "c1",
|
|
411
|
+
"title": "Compact Achievements",
|
|
412
|
+
"collection": "achievement",
|
|
413
|
+
"match": "{\"time\":{\"$gte\":{\"$date\":\"-10y\"},\"$lte\":{\"$date\":\"now\"}}}",
|
|
414
|
+
"aggregate": "[{\"$group\":{\"_id\":{\"player\":\"$player\",\"type\":\"$type\",\"item\":\"$item\"},\"total\":{\"$sum\":\"$total\"},\"time\":{\"$max\":\"$time\"},\"ids\":{\"$push\":\"$_id\"}}},{\"$project\":{\"_id\":0,\"player\":\"$_id.player\",\"type\":\"$_id.type\",\"item\":\"$_id.item\",\"total\":1,\"time\":1,\"extra\":{\"compacted\":\"true\",\"compact\":\"$ids\"}}}]"
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Disparar:
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
GET /v3/compact/c1/execute
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### 10.2 Exemplo avançado — gerar novo `_id` no documento compactado
|
|
425
|
+
|
|
426
|
+
O `$project` zera o `_id` original; um `$addFields` gera um novo `ObjectId` como string (evita colisão ao reinserir). Pipeline (forma legível, antes de escapar):
|
|
427
|
+
|
|
428
|
+
```json
|
|
429
|
+
[
|
|
430
|
+
{ "$group": {
|
|
431
|
+
"_id": { "player": "$player", "type": "$type", "item": "$item" },
|
|
432
|
+
"total": { "$sum": "$total" },
|
|
433
|
+
"time": { "$max": "$time" },
|
|
434
|
+
"ids": { "$push": "$_id" }
|
|
435
|
+
}},
|
|
436
|
+
{ "$project": {
|
|
437
|
+
"_id": 0, "player": "$_id.player", "type": "$_id.type", "item": "$_id.item",
|
|
438
|
+
"total": 1, "time": 1, "extra": { "compacted": "true", "compact": "$ids" }
|
|
439
|
+
}},
|
|
440
|
+
{ "$addFields": {
|
|
441
|
+
"_id": { "$function": {
|
|
442
|
+
"body": "function() { return new ObjectId().toString().replace(/ObjectId\\(\"|\"\\)/g, ''); }",
|
|
443
|
+
"args": [], "lang": "js"
|
|
444
|
+
}}
|
|
445
|
+
}}
|
|
446
|
+
]
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Preservar os `_id` originais em `extra.compact` é a única forma de rastrear o que foi colapsado — o módulo **não** guarda isso automaticamente.
|
|
450
|
+
|
|
451
|
+
### 10.3 Anti-pattern — o que NÃO fazer
|
|
452
|
+
|
|
453
|
+
```json
|
|
454
|
+
POST /v3/compact
|
|
455
|
+
{
|
|
456
|
+
"title": "Limpar logs",
|
|
457
|
+
"collection": "achievement",
|
|
458
|
+
"match": "{}"
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Por que está errado:
|
|
463
|
+
|
|
464
|
+
1. **Falta `aggregate`** → `save` é ignorado silenciosamente, mas o `POST` retorna `201`. Você acha que criou; não criou.
|
|
465
|
+
2. Mesmo que `aggregate` fosse incluído, **`match: "{}"` casa com TODOS os documentos** da coleção. O passo `source.remove(match)` apagaria a coleção inteira antes da reinserção — se o `aggregate` estiver errado, é perda total.
|
|
466
|
+
3. `collection: "achievement"` sem entender o pipeline de substituição transforma "compactação" em "deleção em massa".
|
|
467
|
+
|
|
468
|
+
**Regra:** sempre teste o `match` (quantos documentos casa) e valide o `aggregate` em ambiente controlado **antes** de executar em produção, pois a operação remove os originais.
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Checklist de Configuração
|
|
473
|
+
|
|
474
|
+
- [ ] `collection` preenchido (coleção de origem existe).
|
|
475
|
+
- [ ] `match` preenchido — e **testado**: confira quantos documentos ele casa antes de executar.
|
|
476
|
+
- [ ] `aggregate` preenchido — pipeline válido que **gera documentos substitutos completos** (incluindo `_id`).
|
|
477
|
+
- [ ] Armadilha: se `collection`/`match`/`aggregate` faltarem, o `POST` retorna `201` mas **nada é salvo** — confirme com `GET /v3/compact/{id}`.
|
|
478
|
+
- [ ] Armadilha: `match` amplo (ex.: `{}`) + `aggregate` incorreto = **perda de dados** (remove antes de reinserir, sem transação).
|
|
479
|
+
- [ ] Preserve os `_id` originais no pipeline (ex.: `extra.compact`) se precisar de rastreabilidade — o módulo não faz isso sozinho.
|
|
480
|
+
- [ ] Pós-execução: acompanhe o `compact_log` (`status`/`total`); `finished` = ok, `error` = leia `message`.
|
|
481
|
+
- [ ] Recuperação: se um log ficar preso sem `finish`, novas execuções são bloqueadas — limpe o log e verifique a coleção `<logId>_tmp`.
|
|
482
|
+
- [ ] Trigger opcional: registre `after_finish` na entidade `compact_log` para reagir ao término.
|
|
483
|
+
- [ ] Lembre-se: `match`/`aggregate` viajam como **strings JSON escapadas** no corpo da requisição.
|