funifier-mcp 0.2.25 → 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.
Files changed (211) hide show
  1. package/.cursor/rules/funifier.mdc +38 -41
  2. package/.github/copilot-instructions.md +38 -41
  3. package/AGENTS.md +56 -49
  4. package/README.md +40 -22
  5. package/datasource-funifier-docs/.coverage.json +326 -0
  6. package/datasource-funifier-docs/.validation.json +593 -0
  7. package/datasource-funifier-docs/knowledge/guides/aggregates.md +182 -70
  8. package/datasource-funifier-docs/knowledge/guides/database-access.md +174 -88
  9. package/datasource-funifier-docs/knowledge/guides/java-entities.md +294 -204
  10. package/datasource-funifier-docs/knowledge/guides/java-libraries.md +202 -226
  11. package/datasource-funifier-docs/knowledge/guides/java-managers.md +343 -265
  12. package/datasource-funifier-docs/knowledge/guides/trigger-examples.md +180 -236
  13. package/datasource-funifier-docs/knowledge/guides/triggers-guide.md +273 -191
  14. package/datasource-funifier-docs/knowledge/index.md +5 -2
  15. package/datasource-funifier-docs/knowledge/modules/achievement.md +1126 -28
  16. package/datasource-funifier-docs/knowledge/modules/action-log.md +469 -62
  17. package/datasource-funifier-docs/knowledge/modules/action.md +522 -70
  18. package/datasource-funifier-docs/knowledge/modules/auth.md +718 -69
  19. package/datasource-funifier-docs/knowledge/modules/avatar.md +483 -18
  20. package/datasource-funifier-docs/knowledge/modules/backup.md +603 -25
  21. package/datasource-funifier-docs/knowledge/modules/challenge.md +1048 -220
  22. package/datasource-funifier-docs/knowledge/modules/compact.md +469 -26
  23. package/datasource-funifier-docs/knowledge/modules/competition.md +811 -109
  24. package/datasource-funifier-docs/knowledge/modules/crossword.md +504 -28
  25. package/datasource-funifier-docs/knowledge/modules/csv-data.md +645 -20
  26. package/datasource-funifier-docs/knowledge/modules/custom-object.md +701 -36
  27. package/datasource-funifier-docs/knowledge/modules/database.md +730 -164
  28. package/datasource-funifier-docs/knowledge/modules/folder.md +1011 -77
  29. package/datasource-funifier-docs/knowledge/modules/kpi-formulas.md +410 -15
  30. package/datasource-funifier-docs/knowledge/modules/lastmile.md +568 -29
  31. package/datasource-funifier-docs/knowledge/modules/leaderboard.md +595 -126
  32. package/datasource-funifier-docs/knowledge/modules/level.md +536 -54
  33. package/datasource-funifier-docs/knowledge/modules/lottery.md +809 -76
  34. package/datasource-funifier-docs/knowledge/modules/marketplace.md +688 -17
  35. package/datasource-funifier-docs/knowledge/modules/mystery.md +662 -52
  36. package/datasource-funifier-docs/knowledge/modules/notification.md +564 -26
  37. package/datasource-funifier-docs/knowledge/modules/patterns.md +519 -814
  38. package/datasource-funifier-docs/knowledge/modules/player.md +773 -73
  39. package/datasource-funifier-docs/knowledge/modules/point.md +380 -83
  40. package/datasource-funifier-docs/knowledge/modules/public.md +508 -178
  41. package/datasource-funifier-docs/knowledge/modules/question.md +619 -99
  42. package/datasource-funifier-docs/knowledge/modules/quiz.md +565 -120
  43. package/datasource-funifier-docs/knowledge/modules/scheduler.md +1092 -39
  44. package/datasource-funifier-docs/knowledge/modules/security.md +674 -112
  45. package/datasource-funifier-docs/knowledge/modules/staging.md +742 -19
  46. package/datasource-funifier-docs/knowledge/modules/story.md +565 -29
  47. package/datasource-funifier-docs/knowledge/modules/studio-page.md +470 -144
  48. package/datasource-funifier-docs/knowledge/modules/swap.md +552 -84
  49. package/datasource-funifier-docs/knowledge/modules/team.md +563 -45
  50. package/datasource-funifier-docs/knowledge/modules/trigger.md +876 -134
  51. package/datasource-funifier-docs/knowledge/modules/upload.md +468 -95
  52. package/datasource-funifier-docs/knowledge/modules/virtual-good.md +510 -63
  53. package/datasource-funifier-docs/knowledge/modules/webhook.md +375 -28
  54. package/datasource-funifier-docs/knowledge/modules/websocket.md +459 -26
  55. package/datasource-funifier-docs/knowledge/modules/widget.md +613 -27
  56. package/dist/cli/init.d.ts.map +1 -1
  57. package/dist/cli/init.js +42 -1
  58. package/dist/cli/init.js.map +1 -1
  59. package/dist/cli/init.test.js +74 -3
  60. package/dist/cli/init.test.js.map +1 -1
  61. package/dist/cli/persona.d.ts +3 -0
  62. package/dist/cli/persona.d.ts.map +1 -0
  63. package/dist/cli/persona.js +25 -0
  64. package/dist/cli/persona.js.map +1 -0
  65. package/dist/core/api-client.d.ts +21 -1
  66. package/dist/core/api-client.d.ts.map +1 -1
  67. package/dist/core/api-client.js +154 -1
  68. package/dist/core/api-client.js.map +1 -1
  69. package/dist/core/constants.d.ts +14 -0
  70. package/dist/core/constants.d.ts.map +1 -1
  71. package/dist/core/constants.js +14 -0
  72. package/dist/core/constants.js.map +1 -1
  73. package/dist/core/types/Folder.d.ts +16 -0
  74. package/dist/core/types/Folder.d.ts.map +1 -0
  75. package/dist/core/types/Folder.js +3 -0
  76. package/dist/core/types/Folder.js.map +1 -0
  77. package/dist/core/types/FolderContent.d.ts +10 -0
  78. package/dist/core/types/FolderContent.d.ts.map +1 -0
  79. package/dist/core/types/FolderContent.js +3 -0
  80. package/dist/core/types/FolderContent.js.map +1 -0
  81. package/dist/core/types/FolderContentType.d.ts +10 -0
  82. package/dist/core/types/FolderContentType.d.ts.map +1 -0
  83. package/dist/core/types/FolderContentType.js +3 -0
  84. package/dist/core/types/FolderContentType.js.map +1 -0
  85. package/dist/core/types/FolderLog.d.ts +11 -0
  86. package/dist/core/types/FolderLog.d.ts.map +1 -0
  87. package/dist/core/types/FolderLog.js +3 -0
  88. package/dist/core/types/FolderLog.js.map +1 -0
  89. package/dist/core/types/index.d.ts +4 -0
  90. package/dist/core/types/index.d.ts.map +1 -1
  91. package/dist/core/types/index.js +4 -0
  92. package/dist/core/types/index.js.map +1 -1
  93. package/dist/mcp/bundle.js +121 -87
  94. package/dist/mcp/check-update.d.ts +2 -0
  95. package/dist/mcp/check-update.d.ts.map +1 -0
  96. package/dist/mcp/check-update.js +44 -0
  97. package/dist/mcp/check-update.js.map +1 -0
  98. package/dist/mcp/index.js +5 -2
  99. package/dist/mcp/index.js.map +1 -1
  100. package/dist/mcp/resources/documentation.d.ts +1 -1
  101. package/dist/mcp/resources/documentation.d.ts.map +1 -1
  102. package/dist/mcp/resources/documentation.js +39 -3
  103. package/dist/mcp/resources/documentation.js.map +1 -1
  104. package/dist/mcp/tools/_char-guard.js +1 -1
  105. package/dist/mcp/tools/_char-guard.js.map +1 -1
  106. package/dist/mcp/tools/_fetch-current.d.ts +1 -1
  107. package/dist/mcp/tools/_fetch-current.d.ts.map +1 -1
  108. package/dist/mcp/tools/_fetch-current.js +12 -0
  109. package/dist/mcp/tools/_fetch-current.js.map +1 -1
  110. package/dist/mcp/tools/connect.d.ts.map +1 -1
  111. package/dist/mcp/tools/connect.js +18 -8
  112. package/dist/mcp/tools/connect.js.map +1 -1
  113. package/dist/mcp/tools/database.d.ts.map +1 -1
  114. package/dist/mcp/tools/database.js +59 -47
  115. package/dist/mcp/tools/database.js.map +1 -1
  116. package/dist/mcp/tools/database.test.js +2 -2
  117. package/dist/mcp/tools/database.test.js.map +1 -1
  118. package/dist/mcp/tools/delete.d.ts.map +1 -1
  119. package/dist/mcp/tools/delete.js +33 -3
  120. package/dist/mcp/tools/delete.js.map +1 -1
  121. package/dist/mcp/tools/execute.d.ts.map +1 -1
  122. package/dist/mcp/tools/execute.js +20 -9
  123. package/dist/mcp/tools/execute.js.map +1 -1
  124. package/dist/mcp/tools/folder.d.ts +4 -0
  125. package/dist/mcp/tools/folder.d.ts.map +1 -0
  126. package/dist/mcp/tools/folder.js +68 -0
  127. package/dist/mcp/tools/folder.js.map +1 -0
  128. package/dist/mcp/tools/get.d.ts.map +1 -1
  129. package/dist/mcp/tools/get.js +16 -6
  130. package/dist/mcp/tools/get.js.map +1 -1
  131. package/dist/mcp/tools/index.d.ts +1 -1
  132. package/dist/mcp/tools/index.d.ts.map +1 -1
  133. package/dist/mcp/tools/index.js +5 -1
  134. package/dist/mcp/tools/index.js.map +1 -1
  135. package/dist/mcp/tools/list.d.ts.map +1 -1
  136. package/dist/mcp/tools/list.js +38 -14
  137. package/dist/mcp/tools/list.js.map +1 -1
  138. package/dist/mcp/tools/logs.d.ts.map +1 -1
  139. package/dist/mcp/tools/logs.js +15 -5
  140. package/dist/mcp/tools/logs.js.map +1 -1
  141. package/dist/mcp/tools/save.d.ts.map +1 -1
  142. package/dist/mcp/tools/save.js +26 -4
  143. package/dist/mcp/tools/save.js.map +1 -1
  144. package/dist/mcp/tools/save.test.js +192 -1
  145. package/dist/mcp/tools/save.test.js.map +1 -1
  146. package/dist/mcp/tools/search-docs.d.ts +3 -0
  147. package/dist/mcp/tools/search-docs.d.ts.map +1 -0
  148. package/dist/mcp/tools/search-docs.js +102 -0
  149. package/dist/mcp/tools/search-docs.js.map +1 -0
  150. package/package.json +6 -2
  151. package/skills/acquire-funifier-knowledge/SKILL.md +132 -0
  152. package/skills/acquire-funifier-knowledge/assets/templates/CONCERNS.md +25 -0
  153. package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_ENDPOINTS.md +24 -0
  154. package/skills/acquire-funifier-knowledge/assets/templates/CUSTOM_PAGES.md +24 -0
  155. package/skills/acquire-funifier-knowledge/assets/templates/GAME_MECHANICS.md +35 -0
  156. package/skills/acquire-funifier-knowledge/assets/templates/INTEGRATIONS.md +35 -0
  157. package/skills/acquire-funifier-knowledge/assets/templates/LEADERBOARDS.md +24 -0
  158. package/skills/acquire-funifier-knowledge/assets/templates/OVERVIEW.md +47 -0
  159. package/skills/acquire-funifier-knowledge/assets/templates/PLAYER_MODEL.md +31 -0
  160. package/skills/acquire-funifier-knowledge/assets/templates/SCHEDULERS.md +25 -0
  161. package/skills/acquire-funifier-knowledge/assets/templates/TECHNIQUES_AND_PATTERNS.md +26 -0
  162. package/skills/acquire-funifier-knowledge/assets/templates/TRIGGERS.md +27 -0
  163. package/skills/acquire-funifier-knowledge/references/funifier-inventory-checklist.md +81 -0
  164. package/skills/acquire-funifier-knowledge/references/game-techniques-taxonomy.md +62 -0
  165. package/skills/acquire-funifier-knowledge/references/mcp-call-patterns.md +118 -0
  166. package/skills/funifier/SKILL.md +88 -0
  167. package/skills/funifier/references/configure-security.md +96 -0
  168. package/skills/{funifier-create-action/SKILL.md → funifier/references/create-action.md} +0 -33
  169. package/skills/funifier/references/create-aggregate.md +144 -0
  170. package/skills/funifier/references/create-challenge.md +116 -0
  171. package/skills/funifier/references/create-competition.md +98 -0
  172. package/skills/funifier/references/create-crossword.md +574 -0
  173. package/skills/funifier/references/create-custom-object.md +91 -0
  174. package/skills/funifier/references/create-custom-page.md +135 -0
  175. package/skills/funifier/references/create-folder.md +104 -0
  176. package/skills/funifier/references/create-lastmile.md +643 -0
  177. package/skills/{funifier-create-leaderboard/SKILL.md → funifier/references/create-leaderboard.md} +0 -33
  178. package/skills/funifier/references/create-level.md +94 -0
  179. package/skills/funifier/references/create-lottery.md +913 -0
  180. package/skills/funifier/references/create-mystery.md +769 -0
  181. package/skills/funifier/references/create-notification.md +75 -0
  182. package/skills/{funifier-create-point/SKILL.md → funifier/references/create-point.md} +0 -33
  183. package/skills/funifier/references/create-quiz.md +98 -0
  184. package/skills/funifier/references/create-scheduler.md +141 -0
  185. package/skills/funifier/references/create-story.md +636 -0
  186. package/skills/funifier/references/create-swap.md +95 -0
  187. package/skills/{funifier-create-trigger/SKILL.md → funifier/references/create-trigger.md} +0 -33
  188. package/skills/funifier/references/create-virtual-good.md +96 -0
  189. package/skills/funifier/references/create-webhook.md +72 -0
  190. package/skills/funifier/references/create-websocket.md +71 -0
  191. package/skills/funifier/references/create-widget.md +76 -0
  192. package/skills/funifier/references/debug.md +87 -0
  193. package/skills/funifier/references/help.md +81 -0
  194. package/skills/funifier/references/implement-frontend.md +106 -0
  195. package/skills/funifier/references/import-csv.md +75 -0
  196. package/skills/funifier/references/manage-player.md +82 -0
  197. package/skills/funifier/references/manage-team.md +76 -0
  198. package/skills/funifier/references/upload-file.md +91 -0
  199. package/datasource-funifier-docs/.search-index.json +0 -17318
  200. package/datasource-funifier-docs/.skills-map.json +0 -73
  201. package/skills/funifier-create-aggregate/SKILL.md +0 -127
  202. package/skills/funifier-create-challenge/SKILL.md +0 -88
  203. package/skills/funifier-create-custom-page/SKILL.md +0 -127
  204. package/skills/funifier-create-level/SKILL.md +0 -87
  205. package/skills/funifier-create-quiz/SKILL.md +0 -87
  206. package/skills/funifier-create-scheduler/SKILL.md +0 -127
  207. package/skills/funifier-create-virtual-good/SKILL.md +0 -87
  208. package/skills/funifier-debug/SKILL.md +0 -92
  209. package/skills/funifier-help/SKILL.md +0 -86
  210. package/skills/funifier-implement-frontend/SKILL.md +0 -90
  211. package/skills/funifier-index/SKILL.md +0 -58
@@ -1,40 +1,483 @@
1
- # Compact (Compactação)
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
- ## O que é
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
- Compactação e otimização do armazenamento de dados. Configura rotinas para agrupar registros e reduzir o espaço ocupado por dados históricos, como achievements ou logs, melhorando o tempo de resposta das consultas e a performance geral.
11
+ ---
9
12
 
10
- ## Quando usar
13
+ ## 1. Visão Geral
11
14
 
12
- - Para compactar registros de achievements acumulados
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
- ## Checklist de Configuração no Studio
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
- - [ ] Definir coleções a compactar
19
- - [ ] Configurar critérios de agrupamento
20
- - [ ] Testar impacto na performance
19
+ Papel arquitetural:
21
20
 
22
- ## API Endpoints
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
- ### Listar Compactações
25
- **Método:** GET
26
- **Endpoint:** `/v3/compact`
25
+ Componentes (`com.funifier.engine.compact`):
27
26
 
28
- ### Criar Compactação
29
- **Método:** POST
30
- **Endpoint:** `/v3/compact`
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
- ### Deletar Compactação
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
- ## Validações e Testes
37
+ ---
37
38
 
38
- - [ ] Compactação é executada sem perda de dados
39
- - [ ] Volume de dados é reduzido
40
- - [ ] Consultas mantêm resultados corretos após compactação
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/>&lt;logId&gt;_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.