Skip to content

feat/sprint 9 communications module + buffer#191

Merged
frigini merged 52 commits intomasterfrom
feature/sprint-9-buffer-communications
Apr 13, 2026
Merged

feat/sprint 9 communications module + buffer#191
frigini merged 52 commits intomasterfrom
feature/sprint-9-buffer-communications

Conversation

@frigini
Copy link
Copy Markdown
Owner

@frigini frigini commented Apr 8, 2026

Summary

  • Add detailed Communications module section (email, SMS, push) to Sprint 9 roadmap
  • Document Outbox Pattern architecture for delivery guarantee
  • Map IntegrationEvent handlers (UserRegistered, ProviderVerification, Document events)
  • Add ModuleNames.Communications constant
  • Update roadmap status (Sprint 9E with Communications)

Context

This establishes infrastructure for post-MVP modules (Reviews, Payments, Bookings) that depend on communication capabilities.

Changes

  • docs/roadmap-current.md: +207 lines (Communications module section)
  • docs/roadmap.md: +3 lines (status update)
  • src/Shared/Utilities/Constants/ModuleNames.cs: +6 lines (Communications constant)

Type

  • Documentation

Summary by CodeRabbit

  • Novos Recursos
    • Módulo de Comunicações: envio (e‑mail, SMS, push), templates, consulta paginada de logs e endpoints públicos; novos DTOs/enums e integração outbox.
  • Melhorias
    • Padrão Outbox com worker, prioridades, retries, agendamento e stubs configuráveis; máscaras de dados PII; internacionalização e strings loc.
    • Segurança: cookie antiforgery exposto em GET, HTTPS redirection e cabeçalhos reforçados.
  • Testes
    • Ampliação extensiva de testes unitários/integrados e E2E (Comunicações, Outbox, SearchProviders).
  • Documentação
    • Roadmap atualizado: Sprint 9 encerrada e Módulo de Comunicações incluído.

- Add detailed Communications module section (email, SMS, push)
- Use Outbox Pattern for delivery guarantee
- Define module architecture (full module like Providers/Users)
- Map IntegrationEvent handlers (UserRegistered, ProviderVerification, etc.)
- Add ModuleNames.Communications constant
- Update roadmap status (Sprint 9E with Communications)

This establishes infrastructure for post-MVP modules (Reviews, Payments, Bookings)
that depend on communication capabilities.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fe1af2ca-4dff-4de8-be25-3c040d911645

📥 Commits

Reviewing files that changed from the base of the PR and between 26f8196 and 7590142.

📒 Files selected for processing (1)
  • tests/MeAjudaAi.Integration.Tests/Modules/Users/UserRepositoryIntegrationTests.cs

📝 Walkthrough

Walkthrough

Adiciona o módulo Communications (email, SMS, push) com contratos/DTOs, outbox genérico, entidades/repositórios, processador + worker, handlers que enfileiram mensagens, API de templates/logs, integração no bootstrapper, EF context/migrations, helpers PII, template Razor e extensa suíte de testes.

Changes

Cohort / File(s) Summary
Docs / Roadmap
docs/roadmap-current.md, docs/roadmap.md
Atualiza roadmap em pt‑BR: marca Sprint 9 como encerrada, reformula textos, adiciona especificação e local do Módulo de Comunicações.
Solution & project refs
MeAjudaAi.slnx, src/Bootstrapper/.../MeAjudaAi.ApiService.csproj
Adiciona projetos do módulo Communications à solução e referência no ApiService.
Module registry & migration order
src/Shared/Utilities/Constants/ModuleNames.cs, src/Bootstrapper/.../MigrationExtensions.cs
Renomeia constante NotificationsCommunications e insere Communications na ordem de migração (prioridade 6), deslocando SearchProviders.
Bootstrapper / Startup / Security
src/Bootstrapper/.../Program.cs, .../Extensions/ServiceCollectionExtensions.cs, .../Extensions/SecurityExtensions.cs, .../Middlewares/*, src/Bootstrapper/MeAjudaAi.ApiService/appsettings.json
Registra módulo, seta switch Npgsql, adiciona antiforgery e middleware de cookie, ajusta CORS/limiter/antiforgery, modifica middlewares e adiciona Communications.EnableStubs config.
Contracts — Communications & Shared
src/Contracts/.../Modules/Communications/*, src/Contracts/Shared/*
Novos contratos/DTOs/interfaces (ICommunicationsModuleApi, channel senders), outbox payloads, enums ECommunicationPriority/EOutboxMessageStatus e CommunicationTemplateKeys.
Shared Outbox infra
src/Shared/Database/Outbox/*, src/Shared/Database/DatabaseExtensions.cs, src/Shared/MeAjudaAi.Shared.csproj
Introduz OutboxMessage compartilhado, IOutboxRepository, OutboxProcessorBase, constraints; AddPostgresContext ganhou optionsAction; adiciona EFCore.NamingConventions.
Communications Domain
src/Modules/Communications/Domain/...
Entidades de domínio (OutboxMessage específico, CommunicationLog, EmailTemplate), enum de canais, repositórios e contratos de envio (IEmailSender/ISmsSender/IPushSender).
Communications Infrastructure
src/Modules/Communications/Infrastructure/...
CommunicationsDbContext (schema communications), EF configurations, repositórios (Outbox, EmailTemplate, CommunicationLog), stubs de senders, design‑time factory e DI AddCommunicationsInfrastructure.
Communications Application
src/Modules/Communications/Application/...
ModuleApi (SendEmail/SendSms/SendPush, GetTemplates, GetLogs), OutboxProcessorService, CommunicationsOutboxWorker, vários event handlers que enfileiram mensagens e DI AddCommunicationsApplication.
API Endpoints
src/Modules/Communications/API/...
Extensões e endpoints GET /api/v1/communications/logs e /templates; integração via MapCommunicationsEndpoints() e chamadas em Program.cs.
Templates & resources
src/Shared/Communications/Templates/WelcomeEmail.cshtml, src/Shared/Resources/*
Adiciona template Razor de boas‑vindas e arquivos de recursos de strings (pt/en).
PII & helpers
src/Shared/Utilities/PiiMaskingHelper.cs
Adiciona MaskEmail, MaskPhoneNumber e MaskSensitiveData para logging/masking.
Tests — infra e ajustes
tests/..., tests/MeAjudaAi.Integration.Tests/*, src/Modules/Communications/Tests/*
Grande suíte de testes unitários e de integração para Communications; mudanças na infra de testes (DB por classe, paralelização, seed, JSON helpers) e ajustes em testes existentes.
Locations / CEP clients
src/Modules/Locations/...
Melhora logs/erros, adiciona JsonPropertyName nos DTOs, normaliza base URLs e torna chamadas de provedores mais resilientes.
SearchProviders / ServiceCatalogs
src/Modules/SearchProviders/..., src/Modules/ServiceCatalogs/...
Adiciona GetByServiceIdAsync, handlers para ServiceDeactivated/ServiceActivated e registro dos handlers; pequenas alterações de enum JSON.
CI / Workflow
.github/workflows/ci-backend.yml
Ajustes de readiness/timeouts, remoção de continue‑on‑error, verificação explícita de resultados e inclusão do projeto de testes Communications.
Misc
várias
Ajustes em múltiplos middlewares (SecurityHeaders, CompressionSecurity), BusinessMetrics ctor, design‑time factories, Outbox constraints e pequenos refatores/normalizações.

Sequence Diagram(s)

sequenceDiagram
    participant Event as IntegrationEvent
    participant Handler as EventHandler
    participant OutboxRepo as OutboxRepository
    participant DB as Database
    participant Worker as OutboxWorker
    participant Sender as ProviderSender
    participant LogRepo as CommunicationLogRepo

    Event->>Handler: publica evento (ex: UserRegistered)
    Handler->>OutboxRepo: ExistsByCorrelationIdAsync(correlationId)
    OutboxRepo->>DB: consulta communication_logs
    DB-->>OutboxRepo: resultado
    alt já processado
        OutboxRepo-->>Handler: true (skip)
    else novo
        Handler->>OutboxRepo: AddAsync(OutboxMessage)
        OutboxRepo->>DB: INSERT + SaveChanges
    end

    rect rgba(200,150,255,0.5)
        Worker->>OutboxRepo: GetPendingAsync(batch)
        OutboxRepo->>DB: SELECT pending
        DB-->>OutboxRepo: mensagens

        loop cada mensagem
            Worker->>DB: marcar Processing (transação)
            Worker->>Sender: Dispatch(payload)
            alt sucesso
                Sender-->>Worker: ok
                Worker->>DB: MarkAsSent
                Worker->>LogRepo: AddAsync(CreateSuccess(...))
            else falha
                Sender-->>Worker: erro/falha
                Worker->>DB: MarkAsFailed or ResetToPending
                Worker->>LogRepo: AddAsync(CreateFailure(...)) when final
            end
            Worker->>DB: SaveChanges
        end
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 Pulo leve, rabinho contente,
Enfileirei mensagens num outbox reluzente.
Email, SMS e push a saltitar,
Handlers e workers prontos pra enviar.
Hop — comunicações a celebrar! ✨📬

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sprint-9-buffer-communications

@frigini frigini changed the title docs: add Communications module to Sprint 9 roadmap feat/sprint 9 communications module + buffer Apr 8, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
docs/roadmap-current.md (3)

842-849: Considere adicionar linha em branco antes da tabela de mapeamento.

Para melhor conformidade com padrões Markdown (MD058), adicione uma linha em branco antes da tabela de mapeamento de eventos.

📝 Sugestão de formatação
 **Mapeamento de Integração com Eventos**:
+
 | Evento Existing | Ação de Comunicação |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 842 - 849, Add a blank line immediately
before the events mapping table that begins with the header row "| Evento
Existing | Ação de Comunicação |" so the Markdown has an empty line separating
it from the preceding content (to satisfy MD058); simply insert one newline
above that table block.

940-954: Estimativa de esforço realista.

A estimativa de ~29h (~4 dias) parece adequada para a infraestrutura base. Note que o item 7 tem um pequeno erro de formatação ("7.Stub" deveria ser "7. Stub").

📝 Correção de formatação
-| 7.Stub SendGridEmailService | 4h | Interface channel |
+| 7. Stub SendGridEmailService | 4h | Interface channel |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 940 - 954, Fix the formatting typo in
the roadmap table: change the entry "7.Stub SendGridEmailService" to include a
space after the numeral so it reads "7. Stub SendGridEmailService" to match the
numbering style used for other rows (refer to the table row text "7.Stub
SendGridEmailService" and update it accordingly).

917-936: Blocos de código sem especificador de linguagem.

Os blocos de código nas linhas 922 e 927 não possuem especificador de linguagem, o que pode afetar a renderização (syntax highlighting) no Markdown.

💡 Adicionar especificadores de linguagem
 1. **Processo atual** (síncrono):
-   ```
+   ```text
    User Registered → Save to DB → Send Email (pode falhar silenciosamente)
    ```

 2. **Processo com Outbox** (garantido):
-   ```
+   ```text
    User Registered → Save to Outbox Table → Save to DB → Background Worker processa Outbox
    ```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 917 - 936, The two Markdown code fences
containing the sequences "User Registered → Save to DB → Send Email (pode falhar
silenciosamente)" and "User Registered → Save to Outbox Table → Save to DB →
Background Worker processa Outbox" are missing language specifiers; update both
triple-backtick fences to include a language (e.g., use ```text) so the blocks
render with proper syntax highlighting and consistent styling in the
docs/roadmap-current.md content.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Shared/Utilities/Constants/ModuleNames.cs`:
- Around line 47-49: Replace all references to the removed symbol
ModuleNames.Notifications with the new ModuleNames.Communications in the unit
test class ModuleNamesTests; specifically update the InlineData and assertion
usages that currently use ModuleNames.Notifications (e.g., the InlineData
attributes and parameterized test cases) so they reference
ModuleNames.Communications instead, ensuring compile-time references match the
renamed constant in ModuleNames (public const string Communications).

---

Nitpick comments:
In `@docs/roadmap-current.md`:
- Around line 842-849: Add a blank line immediately before the events mapping
table that begins with the header row "| Evento Existing | Ação de Comunicação
|" so the Markdown has an empty line separating it from the preceding content
(to satisfy MD058); simply insert one newline above that table block.
- Around line 940-954: Fix the formatting typo in the roadmap table: change the
entry "7.Stub SendGridEmailService" to include a space after the numeral so it
reads "7. Stub SendGridEmailService" to match the numbering style used for other
rows (refer to the table row text "7.Stub SendGridEmailService" and update it
accordingly).
- Around line 917-936: The two Markdown code fences containing the sequences
"User Registered → Save to DB → Send Email (pode falhar silenciosamente)" and
"User Registered → Save to Outbox Table → Save to DB → Background Worker
processa Outbox" are missing language specifiers; update both triple-backtick
fences to include a language (e.g., use ```text) so the blocks render with
proper syntax highlighting and consistent styling in the docs/roadmap-current.md
content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5cd4244-8e17-4d8a-8dbf-d64bcf1f16f7

📥 Commits

Reviewing files that changed from the base of the PR and between 15551bc and d88aa20.

📒 Files selected for processing (3)
  • docs/roadmap-current.md
  • docs/roadmap.md
  • src/Shared/Utilities/Constants/ModuleNames.cs

Comment thread src/Shared/Utilities/Constants/ModuleNames.cs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/roadmap-current.md`:
- Around line 825-830: O documento refere-se a TemplateDto.cs mas a API usa
EmailTemplateDto; padronize o nome adotando EmailTemplateDto em todas as menções
do roadmap (substituir TemplateDto.cs por EmailTemplateDto) e atualize a segunda
ocorrência mencionada (as linhas referidas no comentário) para evitar drift
entre documentação e implementação; verifique também referências relacionadas
como TemplateDto/EmailTemplateDto nas listas DTOs e qualquer seção que descreva
o template DTO ou exemplos de payload.
- Around line 966-968: The fenced code block in docs/roadmap-current.md is
missing a language tag (triggering MD040); update the block that currently
starts with ``` and contains "Event Occurred → Save to Outbox Table (Mesma
Transação) → Background Worker processa Outbox" to use a language tag such as
```text so the block becomes ```text ... ```; ensure only the opening fence is
changed and the block content remains unchanged.
- Around line 875-883: The Markdown table block (starting at the header row "|
Evento Existing | Ação de Comunicação |" and including rows for
UserRegisteredIntegrationEvent, ProviderAwaitingVerificationIntegrationEvent,
ProviderVerificationStatusUpdatedIntegrationEvent,
DocumentVerifiedIntegrationEvent, DocumentRejectedIntegrationEvent) lacks
surrounding blank lines and triggers MD058; fix it by inserting a single blank
line immediately before the table header and a single blank line immediately
after the final table row so there is one empty line separating the table from
surrounding paragraphs.
- Around line 659-660: The roadmap contains a scope conflict: the Sprint 9
checklist includes "Módulo Comunicações: Implementar infraestrutura base (outbox
pattern, templates, event handlers)" while Sprint 9's risk/notes define the
sprint as a buffer with no new features; reconcile by either removing or
deferring the "Módulo Comunicações" checklist item from Sprint 9 or by updating
the Sprint 9 risk/notes to explicitly allow this feature; update the text for
"Módulo Comunicações", the Sprint 9 risk/notes, and any duplicated entries noted
around the also-applies section (lines referencing 788-793) so the sprint plan
and risks are consistent and unambiguous.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2b8ee8b-cadb-4030-8816-b99fcb684e9f

📥 Commits

Reviewing files that changed from the base of the PR and between d88aa20 and c393f06.

📒 Files selected for processing (1)
  • docs/roadmap-current.md

Comment thread docs/roadmap-current.md Outdated
Comment thread docs/roadmap-current.md Outdated
Comment thread docs/roadmap-current.md
Comment thread docs/roadmap-current.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

♻️ Duplicate comments (2)
docs/roadmap-current.md (2)

966-969: ⚠️ Potential issue | 🟡 Minor

Especifique a linguagem no bloco de código.

O bloco de código carece de identificador de linguagem, violando MD040. Adicionar text melhora a renderização.

✏️ Correção proposta
-   ```
+   ```text
    Event Occurred → Save to Outbox Table (Mesma Transação) → Background Worker processa Outbox
    ```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 966 - 969, The fenced code block under
the "Processo com Outbox" item is missing a language identifier (MD040); update
the triple-backtick fence that contains "Event Occurred → Save to Outbox Table
(Mesma Transação) → Background Worker processa Outbox" to include the language
token `text` (i.e., change ``` to ```text) so the block is explicitly labeled
and passes the lint rule.

875-884: ⚠️ Potential issue | 🟡 Minor

Adicione linhas em branco ao redor da tabela.

A tabela de mapeamento de eventos carece de linhas em branco antes/depois, violando MD058. Isso pode causar problemas de renderização em alguns parsers Markdown.

✏️ Correção proposta
 |----------------|-------------------|
 | `UserRegisteredIntegrationEvent` | → Enviar email de boas-vindas |
 | `ProviderAwaitingVerificationIntegrationEvent` | → Notificar admin |
 | `ProviderVerificationStatusUpdatedIntegrationEvent` | → Notificar prestador |
 | `DocumentVerifiedIntegrationEvent` | → Notificar prestador |
 | `DocumentRejectedIntegrationEvent` | → Notificar prestador |
+

E adicione uma linha em branco antes da linha 877 (antes do cabeçalho da tabela).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 875 - 884, The Markdown table under the
"Mapeamento de Integração com Eventos" section needs blank lines surrounding it
to satisfy MD058; add at least one empty line immediately before the table
header (before the line with | Evento Existing | Ação de Comunicação |) and at
least one empty line immediately after the final row (after the line with |
`DocumentRejectedIntegrationEvent` | → Notificar prestador |) so the table is
separated from surrounding text and paragraph flow.
🟡 Minor comments (7)
docs/roadmap-current.md-908-910 (1)

908-910: ⚠️ Potential issue | 🟡 Minor

Inconsistência no tipo de retorno: PagedList vs PagedResult.

A documentação indica retorno PagedList<CommunicationLogDto> mas a implementação em ICommunicationsModuleApi.cs usa PagedResult<CommunicationLogDto>. Padronize para evitar confusão.

Com base nos snippets de código fornecidos, a implementação real usa PagedResult. A documentação deve refletir isso.

📝 Correção sugerida
     // Logs (Idempotency check via CorrelationId)
-    Task<Result<PagedList<CommunicationLogDto>>> GetLogsAsync(
+    Task<Result<PagedResult<CommunicationLogDto>>> GetLogsAsync(
         CommunicationLogQuery query, 
         CancellationToken ct = default);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 908 - 910, The docs show GetLogsAsync
returning PagedList<CommunicationLogDto> but the implementation
(ICommunicationsModuleApi.GetLogsAsync) returns
PagedResult<CommunicationLogDto>; update the documentation snippet to use
PagedResult<CommunicationLogDto> to match the implementation (or, alternatively,
change the implementation to PagedList if that is the intended canonical type),
ensuring the signature in the docs and the code for GetLogsAsync and related
type references are consistent.
docs/roadmap-current.md-889-889 (1)

889-889: ⚠️ Potential issue | 🟡 Minor

Corrigir ordem do enum CommunicationPriority na documentação.

A documentação mostra { High, Normal, Low }, mas a implementação real em src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs define como Low = 0, Normal = 1, High = 2. A documentação deve refletir a ordem correta com os valores numéricos explícitos.

Correção sugerida
-public enum CommunicationPriority { High, Normal, Low }
+public enum CommunicationPriority { Low = 0, Normal = 1, High = 2 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` at line 889, The documentation's
CommunicationPriority enum ordering is incorrect; update the docs entry for
CommunicationPriority to match the implementation by listing the members as Low
= 0, Normal = 1, High = 2 (in that order) and include the numeric values
explicitly so the docs reflect the enum in ICommunicationsModuleApi
(CommunicationPriority).
src/Modules/Communications/Domain/Entities/CommunicationLog.cs-61-67 (1)

61-67: ⚠️ Potential issue | 🟡 Minor

Valide attemptCount para impedir estados inválidos.

CreateSuccess e CreateFailure aceitam attemptCount negativo, o que quebra a semântica de tentativas realizadas.

🛠️ Sugestão de ajuste
     public static CommunicationLog CreateSuccess(
         string correlationId,
         ECommunicationChannel channel,
         string recipient,
         int attemptCount,
         Guid? outboxMessageId = null,
         string? templateKey = null)
     {
         ArgumentException.ThrowIfNullOrWhiteSpace(correlationId);
         ArgumentException.ThrowIfNullOrWhiteSpace(recipient);
+        if (attemptCount < 0)
+            throw new ArgumentOutOfRangeException(nameof(attemptCount), "Attempt count cannot be negative.");

         return new CommunicationLog
         {
@@
     public static CommunicationLog CreateFailure(
         string correlationId,
         ECommunicationChannel channel,
         string recipient,
         string errorMessage,
         int attemptCount,
         Guid? outboxMessageId = null,
         string? templateKey = null)
     {
         ArgumentException.ThrowIfNullOrWhiteSpace(correlationId);
         ArgumentException.ThrowIfNullOrWhiteSpace(recipient);
         ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage);
+        if (attemptCount < 0)
+            throw new ArgumentOutOfRangeException(nameof(attemptCount), "Attempt count cannot be negative.");

         return new CommunicationLog
         {

Also applies to: 87-94

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Domain/Entities/CommunicationLog.cs` around lines
61 - 67, Valide o parâmetro attemptCount em CommunicationLog.CreateSuccess e
CommunicationLog.CreateFailure para impedir valores negativos: dentro de cada
método (CreateSuccess, CreateFailure) verifique if (attemptCount < 0) e lance
ArgumentOutOfRangeException(nameof(attemptCount)) com mensagem clara; isso
garante que tentativas negativas não sejam aceitas e mantém a semântica de
tentativas realizadas consistente entre ambos os métodos.
src/Modules/Communications/Domain/Entities/EmailTemplate.cs-66-88 (1)

66-88: ⚠️ Potential issue | 🟡 Minor

Normalize e valide language e overrideKey na criação.

Hoje é possível criar template com language em branco; além disso, overrideKey com espaços vira "" em vez de null.

🧩 Sugestão de ajuste
     public static EmailTemplate Create(
         string templateKey,
         string subject,
         string htmlBody,
         string textBody,
         string language = "pt-BR",
         string? overrideKey = null,
         bool isSystemTemplate = false)
     {
         ArgumentException.ThrowIfNullOrWhiteSpace(templateKey);
         ArgumentException.ThrowIfNullOrWhiteSpace(subject);
         ArgumentException.ThrowIfNullOrWhiteSpace(htmlBody);
         ArgumentException.ThrowIfNullOrWhiteSpace(textBody);
+        ArgumentException.ThrowIfNullOrWhiteSpace(language);
+
+        var normalizedOverrideKey = string.IsNullOrWhiteSpace(overrideKey)
+            ? null
+            : overrideKey.ToLowerInvariant().Trim();

         return new EmailTemplate
         {
             TemplateKey = templateKey.ToLowerInvariant().Trim(),
-            OverrideKey = overrideKey?.ToLowerInvariant().Trim(),
+            OverrideKey = normalizedOverrideKey,
             Subject = subject,
             HtmlBody = htmlBody,
             TextBody = textBody,
-            Language = language,
+            Language = language.Trim(),
             IsActive = true,
             Version = 1,
             IsSystemTemplate = isSystemTemplate
         };
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Domain/Entities/EmailTemplate.cs` around lines 66
- 88, In EmailTemplate.Create, validate and normalize the language and
overrideKey: call ArgumentException.ThrowIfNullOrWhiteSpace(language) (or
otherwise reject empty/whitespace) and then set Language =
language.ToLowerInvariant().Trim(); for overrideKey, normalize and convert
empty/whitespace to null by using OverrideKey =
string.IsNullOrWhiteSpace(overrideKey) ? null :
overrideKey.ToLowerInvariant().Trim(); update the Create method to perform these
checks and assignments so language cannot be blank and overrideKey won't become
an empty string.
src/Modules/Communications/API/Extensions.cs-33-35 (1)

33-35: ⚠️ Potential issue | 🟡 Minor

Padronize os comentários deste grupo de endpoints para português.

Os placeholders daqui ficaram em inglês, diferente da convenção adotada para comentários/XML em arquivos C#.

Based on learnings, comentários e XML docs em arquivos C# devem ficar em português.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/API/Extensions.cs` around lines 33 - 35, Padronize
os comentários no grupo de endpoints para português: substitua as linhas de
comentário que usam inglês (por exemplo as placeholders "// Endpoints will be
mapped here" e "// group.MapGet("/templates", ...);", "// group.MapGet("/logs",
...);") por textos em português mantendo o mesmo contexto (ex.: "// Endpoints
serão mapeados aqui" e "// group.MapGet("/templates", ...); // Exemplo: obter
templates"), e ajuste qualquer XML doc no mesmo arquivo Extensions.cs para
seguir a convenção em português; localize o bloco que referencia o objeto group
e atualize apenas os comentários sem alterar a lógica do mapagem de endpoints.
src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs-58-60 (1)

58-60: ⚠️ Potential issue | 🟡 Minor

Padronize o XML doc para PT-BR.

O trecho Idempotency check via CorrelationId ficou misturado em inglês no meio da documentação pública.

Based on learnings, comentários e XML docs em arquivos C# devem ficar em português.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs`
around lines 58 - 60, Atualize o comentário XML público na interface
ICommunicationsModuleApi para ficar totalmente em português: substitua o trecho
misto "Idempotency check via CorrelationId" por uma frase em PT-BR, por exemplo
"Verificação de idempotência via Identificador de Correlação (CorrelationId)" ou
similar, garantindo que a tag <summary> da operação que "Obtém logs de
comunicação paginados" esteja 100% em português sem termos soltos em inglês.
src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs-46-47 (1)

46-47: ⚠️ Potential issue | 🟡 Minor

Não ignore channel inválido silenciosamente.

Se o parse falhar, o filtro simplesmente some e a consulta passa a retornar logs de todos os canais. Isso mascara erro de input e entrega uma página muito mais ampla do que a pedida. Valide o valor antes de chegar aqui, ou retorne erro/resultado vazio quando ele for inválido.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs`
around lines 46 - 47, O filtro de canal em CommunicationLogRepository atualmente
ignora silently valores inválidos porque o resultado de
Enum.TryParse<ECommunicationChannel> é descartado; modifique o trecho que usa
channel e Enum.TryParse para validar o input: se TryParse falhar, ou lance um
ArgumentException/BadRequest com mensagem clara sobre o canal inválido, ou
aplique um filtro que garanta resultado vazio (por exemplo atribuir query =
query.Where(x => false)) e retorne imediatamente; preserve o comportamento atual
quando TryParse succeeds (usar ch no Where).
🧹 Nitpick comments (1)
src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs (1)

37-38: Ajuste de índice pode melhorar o throughput do polling do Outbox.

Nas consultas de pendentes (filtro por status + agendamento e ordenação por prioridade/criação), o desenho atual pode gerar mais custo de plano em volume alto. Vale considerar um índice composto alinhado a esse padrão.

💡 Proposta de ajuste
-        builder.HasIndex(x => new { x.Status, x.Priority, x.CreatedAt });
-        builder.HasIndex(x => x.ScheduledAt);
+        builder.HasIndex(x => new { x.Status, x.ScheduledAt, x.Priority, x.CreatedAt });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs`
around lines 37 - 38, Substitua os índices atuais por um índice composto
alinhado ao padrão de consulta de polling do Outbox: remova/evite o índice
separado em ScheduledAt e crie um único índice composto usando
builder.HasIndex(x => new { x.Status, x.ScheduledAt, x.Priority, x.CreatedAt })
para que o engine possa filtrar por Status+ScheduledAt e já aproveitar a
ordenação por Priority/CreatedAt, melhorando throughput em alto volume.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs`:
- Around line 28-34: O código no ProviderActivatedIntegrationEventHandler usa
logRepository.ExistsByCorrelationIdAsync seguido de logRepository.AddAsync
(também em outra ocorrência nas linhas 53-54), o que causa TOCTOU e permite
enfileiramento duplicado sob concorrência; altere a lógica para uma operação
atômica (por exemplo implementar e usar
logRepository.AddIfNotExistsAsync/CaptureUniqueInsertAsync ou executar AddAsync
dentro de uma transação com constraint única no campo CorrelationId) e, como
alternativa/backup, trate a exceção de chave duplicada ao chamar AddAsync para
ignorar o segundo inserto; aplique a mudança nas duas ocorrências referenciadas
no ProviderActivatedIntegrationEventHandler para garantir idempotência.

In
`@src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs`:
- Around line 26-49: Ocorre que o correlationId só é usado no pré-check
(logRepository.ExistsByCorrelationIdAsync) mas não é gravado com a mensagem de
Outbox, permitindo duplicados; ajuste a criação de OutboxMessage
(OutboxMessage.Create) para incluir o correlationId — por exemplo adicionando-o
aos metadados/headers do OutboxMessage e também no payload serializado para que
o processor e o CommunicationLog possam reconstruir/associar o id; garanta que o
mesmo correlationId gerado (`var correlationId =
$"{TemplateKey}:{integrationEvent.UserId}";`) seja passado tanto em payload
quanto nos campos/metadados de OutboxMessage antes de enfileirar.

In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`:
- Around line 47-52: O método GetTemplatesAsync está retornando sempre sucesso
com lista vazia, escondendo falhas; atualize a implementação de
GetTemplatesAsync para consultar o repositório real (ex.: usar
IEmailTemplateRepository.GetAll() ou um novo método GetByKeys(...) se GetAll não
existir), mapear os resultados para EmailTemplateDto e retornar
Result.Success(comLista) ou, se a operação não puder ser feita ainda,
lançar/retornar um Result.Failure claro (ou NotImplementedException) em vez de
sempre retornar sucesso vazio; localize a lógica em GetTemplatesAsync na classe
CommunicationsModuleApi e ajuste para usar o repositório e o mapper existentes
para obter templates reais.
- Around line 32-36: Add fail-fast guard clauses at the start of SendEmailAsync
and the other public API methods in CommunicationsModuleApi (the async public
methods around the mentioned ranges) to validate inputs: throw
ArgumentNullException when DTO/query parameters like email are null, throw
ArgumentOutOfRangeException or ArgumentException for invalid pagination or
numeric arguments, and/or return an appropriate failed Result when your codebase
prefers Result-based errors; place these checks at the top of SendEmailAsync
(and the other public methods) before any processing or enqueuing to prevent
null/invalid payloads from being enqueued.

In `@src/Modules/Communications/Application/Services/Email/StubEmailService.cs`:
- Around line 18-20: The info log in StubEmailService is exposing PII
(message.To) and possibly sensitive message.Subject; update the logging in the
StubEmailService (where logger.LogInformation is called) to stop including
message.To and message.Subject—log only non-sensitive metadata such as
message.Body.Length or a masked/aggregate indicator (e.g., recipient count or
"recipient masked") and keep the existing descriptive text, ensuring no raw
email addresses or subject text are written to logs.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Around line 27-35: O serviço OutboxProcessorService não está persistindo
CommunicationLog após cada tentativa de entrega; para corrigir, após cada
send/attempt path in OutboxProcessorService (both the success and failure
branches around where OutboxMessage is updated — see the send logic between the
earlier constructor and the 53-82 region) create a CommunicationLog instance
populated with correlationId, outboxMessageId, status (Succeeded/Failed),
provider details, timestamp and error info when present, and persist it via the
repository (use _logRepository.AddAsync / CreateAsync / SaveAsync as
appropriate) and commit/save changes; ensure the existing idempotency check
(ExistsByCorrelationIdAsync) and GetLogsAsync will reflect these persisted logs
so delivery state is accurate.

In `@src/Modules/Communications/Domain/Entities/OutboxMessage.cs`:
- Around line 100-124: Proteja as transições de estado em MarkAsSent e
MarkAsFailed impedindo mutações quando a entidade já está num estado terminal:
verifique Status contra EOutboxMessageStatus.Sent e EOutboxMessageStatus.Failed
no início de MarkAsSent e MarkAsFailed, e em caso de estado terminal lance uma
InvalidOperationException (ou retorne sem alterar) para evitar incrementar
RetryCount, alterar SentAt, ErrorMessage ou chamar MarkAsUpdated em estados
finais; aplique essa verificação antes de qualquer modificação das propriedades
Status, RetryCount, SentAt, ErrorMessage ou chamada a MarkAsUpdated para
garantir integridade concorrente.

In
`@src/Modules/Communications/Infrastructure/MeAjudaAi.Modules.Communications.Infrastructure.csproj`:
- Around line 32-34: Crie uma factory de DbContext em tempo de design chamada
CommunicationsDbContextFactory dentro de Persistence
(src/Modules/Communications/Infrastructure/Persistence/) que herde de
BaseDesignTimeDbContextFactory<CommunicationsDbContext>; a factory deve expor a
classe pública CommunicationsDbContextFactory :
BaseDesignTimeDbContextFactory<CommunicationsDbContext> para permitir que o
tooling do EF descubra e crie uma instância de CommunicationsDbContext em tempo
de design (siga o mesmo padrão usado pelos módulos
Documents/Locations/Providers/SearchProviders/ServiceCatalogs/Users), assegure o
namespace correto e que o projeto de Infrastructure compile com essa nova classe
para que dotnet ef migrations add funcione.

In
`@src/Modules/Communications/Infrastructure/Persistence/CommunicationsDbContext.cs`:
- Around line 33-40: Os overrides GetDomainEventsAsync e ClearDomainEvents estão
vazios, portanto BaseDbContext.SaveChangesAsync perde/descarta eventos de
domínio; implemente aqui a varredura do ChangeTracker para coletar eventos das
entidades (ex.: procurar entidades que implementam IHasDomainEvents / têm uma
coleção DomainEvents) e retornar/remover esses eventos em
GetDomainEventsAsync/ClearDomainEvents respectivamente, ou se o módulo realmente
não usa eventos adicione um comentário/XML doc em português explicando
explicitamente que não há eventos a processar para evitar silent no-op.

In
`@src/Modules/Communications/Infrastructure/Persistence/Configurations/EmailTemplateConfiguration.cs`:
- Line 37: The current unique index defined via builder.HasIndex(x => new {
x.TemplateKey, x.Language, x.OverrideKey }).IsUnique() doesn't enforce
uniqueness for default templates because OverrideKey is nullable; update the
configuration in EmailTemplateConfiguration to handle NULLs explicitly: if
targeting PostgreSQL 15+ call AreNullsDistinct(false) on that index to treat
NULLs as equal (use builder.HasIndex(...).IsUnique().AreNullsDistinct(false));
if you need older PostgreSQL compatibility replace the single index with two
filtered unique indexes—one unique index on (TemplateKey, Language) filtered
where OverrideKey IS NULL and another unique index on (TemplateKey, Language,
OverrideKey) filtered where OverrideKey IS NOT NULL—so TemplateKey/Language have
at most one NULL OverrideKey and non-null overrides remain uniquely constrained.

In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs`:
- Around line 20-25: GetPendingAsync currently reads messages with Status ==
EOutboxMessageStatus.Pending but does not atomically claim them, so concurrent
processors can pick the same rows; modify GetPendingAsync to claim rows inside a
DB transaction (e.g., UPDATE ... OUTPUT/RETURNING or SELECT ... FOR UPDATE SKIP
LOCKED depending on the DB) so the method atomically sets Status to a
"Processing" state (or writes ClaimOwner/ClaimedAt) for the selected messages
before returning them. Locate GetPendingAsync in OutboxMessageRepository,
perform the selection ordered by Priority/CreatedAt and ScheduledAt filter
within a transaction, use an UPDATE that marks those rows as claimed and returns
the updated entities (or use row locking with SKIP LOCKED) to ensure no two
workers can fetch the same messages concurrently.

In `@src/Modules/Communications/Infrastructure/Services/EmailSenderStub.cs`:
- Line 14: The stub is logging potential PII (message.To and message.Subject) in
EmailSenderStub via logger.LogInformation; remove or redact those fields:
replace the detailed template with a non-PII message (e.g., "Stub email sent"
plus a non-identifying id or masked/hashed recipient if you must correlate), or
log only metadata like message length or a boolean flag; update the
LogInformation call in EmailSenderStub to avoid including message.To and
message.Subject directly.

In `@src/Modules/Communications/Infrastructure/Services/PushSenderStub.cs`:
- Line 14: O log atual em PushSenderStub (logger.LogInformation) inclui o campo
sensível notification.DeviceToken; remova o DeviceToken do template de log ou
substitua-o por uma versão não recuperável (por exemplo, um hash/mascara) e
preserve apenas metadados não sensíveis como notification.Title; atualize a
chamada logger.LogInformation para não persistir notification.DeviceToken
diretamente, garantindo que qualquer identificação do token seja anonimizada
antes de qualquer logging.

In `@src/Modules/Communications/Infrastructure/Services/SmsSenderStub.cs`:
- Line 14: O log atual em SmsSenderStub (logger.LogInformation("[STUB SMS
SENDER] To: {To} | Message: {Msg}", message.PhoneNumber, message.Body)) está
expondo PII e conteúdo sensível; altere a chamada de log para não gravar o corpo
do SMS e substituir/anonimizar o número (por exemplo, mascarar mostrando só
últimos 2-4 dígitos ou usar um hash irreversível como SHA‑256 do PhoneNumber) e
mantenha apenas metadados não sensíveis (p.ex. "redacted" ou o hash/mascara) no
texto de log para indicar envio sem expor dados brutos na classe SmsSenderStub e
em qualquer método que use logger.LogInformation.

---

Minor comments:
In `@docs/roadmap-current.md`:
- Around line 908-910: The docs show GetLogsAsync returning
PagedList<CommunicationLogDto> but the implementation
(ICommunicationsModuleApi.GetLogsAsync) returns
PagedResult<CommunicationLogDto>; update the documentation snippet to use
PagedResult<CommunicationLogDto> to match the implementation (or, alternatively,
change the implementation to PagedList if that is the intended canonical type),
ensuring the signature in the docs and the code for GetLogsAsync and related
type references are consistent.
- Line 889: The documentation's CommunicationPriority enum ordering is
incorrect; update the docs entry for CommunicationPriority to match the
implementation by listing the members as Low = 0, Normal = 1, High = 2 (in that
order) and include the numeric values explicitly so the docs reflect the enum in
ICommunicationsModuleApi (CommunicationPriority).

In `@src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs`:
- Around line 58-60: Atualize o comentário XML público na interface
ICommunicationsModuleApi para ficar totalmente em português: substitua o trecho
misto "Idempotency check via CorrelationId" por uma frase em PT-BR, por exemplo
"Verificação de idempotência via Identificador de Correlação (CorrelationId)" ou
similar, garantindo que a tag <summary> da operação que "Obtém logs de
comunicação paginados" esteja 100% em português sem termos soltos em inglês.

In `@src/Modules/Communications/API/Extensions.cs`:
- Around line 33-35: Padronize os comentários no grupo de endpoints para
português: substitua as linhas de comentário que usam inglês (por exemplo as
placeholders "// Endpoints will be mapped here" e "// group.MapGet("/templates",
...);", "// group.MapGet("/logs", ...);") por textos em português mantendo o
mesmo contexto (ex.: "// Endpoints serão mapeados aqui" e "//
group.MapGet("/templates", ...); // Exemplo: obter templates"), e ajuste
qualquer XML doc no mesmo arquivo Extensions.cs para seguir a convenção em
português; localize o bloco que referencia o objeto group e atualize apenas os
comentários sem alterar a lógica do mapagem de endpoints.

In `@src/Modules/Communications/Domain/Entities/CommunicationLog.cs`:
- Around line 61-67: Valide o parâmetro attemptCount em
CommunicationLog.CreateSuccess e CommunicationLog.CreateFailure para impedir
valores negativos: dentro de cada método (CreateSuccess, CreateFailure)
verifique if (attemptCount < 0) e lance
ArgumentOutOfRangeException(nameof(attemptCount)) com mensagem clara; isso
garante que tentativas negativas não sejam aceitas e mantém a semântica de
tentativas realizadas consistente entre ambos os métodos.

In `@src/Modules/Communications/Domain/Entities/EmailTemplate.cs`:
- Around line 66-88: In EmailTemplate.Create, validate and normalize the
language and overrideKey: call
ArgumentException.ThrowIfNullOrWhiteSpace(language) (or otherwise reject
empty/whitespace) and then set Language = language.ToLowerInvariant().Trim();
for overrideKey, normalize and convert empty/whitespace to null by using
OverrideKey = string.IsNullOrWhiteSpace(overrideKey) ? null :
overrideKey.ToLowerInvariant().Trim(); update the Create method to perform these
checks and assignments so language cannot be blank and overrideKey won't become
an empty string.

In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs`:
- Around line 46-47: O filtro de canal em CommunicationLogRepository atualmente
ignora silently valores inválidos porque o resultado de
Enum.TryParse<ECommunicationChannel> é descartado; modifique o trecho que usa
channel e Enum.TryParse para validar o input: se TryParse falhar, ou lance um
ArgumentException/BadRequest com mensagem clara sobre o canal inválido, ou
aplique um filtro que garanta resultado vazio (por exemplo atribuir query =
query.Where(x => false)) e retorne imediatamente; preserve o comportamento atual
quando TryParse succeeds (usar ch no Where).

---

Duplicate comments:
In `@docs/roadmap-current.md`:
- Around line 966-969: The fenced code block under the "Processo com Outbox"
item is missing a language identifier (MD040); update the triple-backtick fence
that contains "Event Occurred → Save to Outbox Table (Mesma Transação) →
Background Worker processa Outbox" to include the language token `text` (i.e.,
change ``` to ```text) so the block is explicitly labeled and passes the lint
rule.
- Around line 875-884: The Markdown table under the "Mapeamento de Integração
com Eventos" section needs blank lines surrounding it to satisfy MD058; add at
least one empty line immediately before the table header (before the line with |
Evento Existing | Ação de Comunicação |) and at least one empty line immediately
after the final row (after the line with | `DocumentRejectedIntegrationEvent` |
→ Notificar prestador |) so the table is separated from surrounding text and
paragraph flow.

---

Nitpick comments:
In
`@src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs`:
- Around line 37-38: Substitua os índices atuais por um índice composto alinhado
ao padrão de consulta de polling do Outbox: remova/evite o índice separado em
ScheduledAt e crie um único índice composto usando builder.HasIndex(x => new {
x.Status, x.ScheduledAt, x.Priority, x.CreatedAt }) para que o engine possa
filtrar por Status+ScheduledAt e já aproveitar a ordenação por
Priority/CreatedAt, melhorando throughput em alto volume.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 924e8f0e-1f8a-466f-8468-7ea2e6959192

📥 Commits

Reviewing files that changed from the base of the PR and between c393f06 and e61a444.

⛔ Files ignored due to path filters (20)
  • src/Aspire/MeAjudaAi.AppHost/packages.lock.json is excluded by !**/packages.lock.json
  • src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Communications/API/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Communications/Application/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Communications/Domain/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Communications/Infrastructure/Persistence/Migrations/20260409000512_InitialCommunications.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/Communications/Infrastructure/Persistence/Migrations/20260409000512_InitialCommunications.cs is excluded by !**/Migrations/**
  • src/Modules/Communications/Infrastructure/Persistence/Migrations/CommunicationsDbContextModelSnapshot.cs is excluded by !**/Migrations/**
  • src/Modules/Communications/Infrastructure/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Documents/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Locations/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Providers/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/SearchProviders/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/ServiceCatalogs/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • src/Modules/Users/Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.ApiService.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Architecture.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.E2E.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Integration.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Shared.Tests/packages.lock.json is excluded by !**/packages.lock.json
📒 Files selected for processing (56)
  • MeAjudaAi.slnx
  • docs/roadmap-current.md
  • src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj
  • src/Bootstrapper/MeAjudaAi.ApiService/MigrationExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
  • src/Contracts/Contracts/Modules/Communications/Channels/IEmailChannel.cs
  • src/Contracts/Contracts/Modules/Communications/Channels/IPushChannel.cs
  • src/Contracts/Contracts/Modules/Communications/Channels/ISmsChannel.cs
  • src/Contracts/Contracts/Modules/Communications/DTOs/CommunicationLogDto.cs
  • src/Contracts/Contracts/Modules/Communications/DTOs/EmailMessageDto.cs
  • src/Contracts/Contracts/Modules/Communications/DTOs/EmailTemplateDto.cs
  • src/Contracts/Contracts/Modules/Communications/DTOs/PushMessageDto.cs
  • src/Contracts/Contracts/Modules/Communications/DTOs/SmsMessageDto.cs
  • src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs
  • src/Contracts/Contracts/Modules/Communications/Queries/CommunicationLogQuery.cs
  • src/Modules/Communications/API/Extensions.cs
  • src/Modules/Communications/API/MeAjudaAi.Modules.Communications.API.csproj
  • src/Modules/Communications/Application/Extensions.cs
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs
  • src/Modules/Communications/Application/MeAjudaAi.Modules.Communications.Application.csproj
  • src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs
  • src/Modules/Communications/Application/Services/Email/IEmailService.cs
  • src/Modules/Communications/Application/Services/Email/StubEmailService.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Domain/Entities/CommunicationLog.cs
  • src/Modules/Communications/Domain/Entities/EmailTemplate.cs
  • src/Modules/Communications/Domain/Entities/OutboxMessage.cs
  • src/Modules/Communications/Domain/Enums/ECommunicationChannel.cs
  • src/Modules/Communications/Domain/Enums/ECommunicationPriority.cs
  • src/Modules/Communications/Domain/Enums/EOutboxMessageStatus.cs
  • src/Modules/Communications/Domain/MeAjudaAi.Modules.Communications.Domain.csproj
  • src/Modules/Communications/Domain/Repositories/ICommunicationLogRepository.cs
  • src/Modules/Communications/Domain/Repositories/IEmailTemplateRepository.cs
  • src/Modules/Communications/Domain/Repositories/IOutboxMessageRepository.cs
  • src/Modules/Communications/Domain/Services/IEmailSender.cs
  • src/Modules/Communications/Domain/Services/IPushSender.cs
  • src/Modules/Communications/Domain/Services/ISmsSender.cs
  • src/Modules/Communications/Infrastructure/Extensions.cs
  • src/Modules/Communications/Infrastructure/MeAjudaAi.Modules.Communications.Infrastructure.csproj
  • src/Modules/Communications/Infrastructure/Persistence/CommunicationsDbContext.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/CommunicationLogConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/EmailTemplateConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs
  • src/Modules/Communications/Infrastructure/Services/EmailSenderStub.cs
  • src/Modules/Communications/Infrastructure/Services/PushSenderStub.cs
  • src/Modules/Communications/Infrastructure/Services/SmsSenderStub.cs
  • src/Shared/Communications/Templates/WelcomeEmail.cshtml
  • tests/MeAjudaAi.Integration.Tests/Modules/Documents/DocumentsEndpointsTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Locations/GeographicRestrictionTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/ServiceCatalogs/ServiceCatalogsEndpointsTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Users/UsersEndpointsTests.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Constants/ModuleNamesTests.cs
✅ Files skipped from review due to trivial changes (11)
  • src/Modules/Communications/Domain/Enums/ECommunicationPriority.cs
  • src/Modules/Communications/API/MeAjudaAi.Modules.Communications.API.csproj
  • src/Contracts/Contracts/Modules/Communications/DTOs/PushMessageDto.cs
  • src/Contracts/Contracts/Modules/Communications/DTOs/EmailMessageDto.cs
  • src/Contracts/Contracts/Modules/Communications/Queries/CommunicationLogQuery.cs
  • src/Modules/Communications/Domain/Enums/ECommunicationChannel.cs
  • src/Modules/Communications/Domain/Enums/EOutboxMessageStatus.cs
  • src/Shared/Communications/Templates/WelcomeEmail.cshtml
  • src/Contracts/Contracts/Modules/Communications/DTOs/CommunicationLogDto.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj
  • src/Contracts/Contracts/Modules/Communications/DTOs/EmailTemplateDto.cs

Comment thread src/Modules/Communications/Application/Services/Email/StubEmailService.cs Outdated
Comment thread src/Modules/Communications/Infrastructure/Services/EmailSenderStub.cs Outdated
Comment thread src/Modules/Communications/Infrastructure/Services/PushSenderStub.cs Outdated
Comment thread src/Modules/Communications/Infrastructure/Services/SmsSenderStub.cs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs (2)

32-36: ⚠️ Potential issue | 🟠 Major

Adicione validações fail-fast nas entradas públicas

Os métodos aceitam parâmetros sem validação, permitindo enfileirar payload inválido ou consultar logs com paginação inconsistente.

🔧 Patch objetivo
 public async Task<Result<Guid>> SendEmailAsync(
     EmailMessageDto email,
     CommunicationPriority priority = CommunicationPriority.Normal,
     CancellationToken ct = default)
 {
+    ArgumentNullException.ThrowIfNull(email);
     var payload = JsonSerializer.Serialize(email);
@@
 public async Task<Result<Guid>> SendSmsAsync(SmsMessageDto sms, CancellationToken ct = default)
 {
+    ArgumentNullException.ThrowIfNull(sms);
     var payload = JsonSerializer.Serialize(sms);
@@
 public async Task<Result<Guid>> SendPushAsync(PushMessageDto push, CancellationToken ct = default)
 {
+    ArgumentNullException.ThrowIfNull(push);
     var payload = JsonSerializer.Serialize(push);
@@
 public async Task<Result<PagedResult<CommunicationLogDto>>> GetLogsAsync(
     CommunicationLogQuery query,
     CancellationToken ct = default)
 {
+    ArgumentNullException.ThrowIfNull(query);
+    if (query.PageNumber < 1) throw new ArgumentOutOfRangeException(nameof(query.PageNumber));
+    if (query.PageSize < 1) throw new ArgumentOutOfRangeException(nameof(query.PageSize));
+
     var (items, totalCount) = await logRepository.SearchAsync(

Also applies to: 56-57, 70-71, 84-86

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`
around lines 32 - 36, Adicione validações fail-fast em todos os métodos públicos
do módulo (por exemplo SendEmailAsync) para validar imediatamente parâmetros de
entrada: verifique email != null e campos obrigatórios dentro de EmailMessageDto
(destinatários, subject, body), valide que priority é um valor válido do enum
CommunicationPriority, e para métodos de paginação valide page/pageSize > 0 e
limites máximos; ao detectar entrada inválida retorne de imediato um Result de
falha com mensagem clara em vez de enfileirar ou prosseguir. Implemente essas
checagens no início de SendEmailAsync e nas demais APIs públicas listadas no
arquivo (aplique mesma lógica para os métodos aos quais o comentário se refere)
usando os símbolos existentes (SendEmailAsync, EmailMessageDto,
CommunicationPriority) e mantenha comportamento assíncrono/assinatura
(Task<Result<...>>) retornando Result.Failure apropriado.

49-54: ⚠️ Potential issue | 🟠 Major

GetTemplatesAsync não deve retornar sucesso vazio de forma permanente

Retornar sucesso com lista vazia fixa esconde lacuna funcional para consumidores que dependem de templates reais. Ou implemente a leitura no repositório, ou falhe explicitamente até concluir a feature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`
around lines 49 - 54, O método GetTemplatesAsync atualmente devolve sempre um
Success com lista vazia; substitua esse placeholder por uma implementação real:
invoque o repositório de templates (ex.:
_emailTemplateRepository.GetAllAsync(ct) ou adicione um método GetAll/FindByKeys
em IEmailTemplateRepository) e mapeie os resultados para EmailTemplateDto
retornando Result.Success(list) ou, enquanto a persistência não existir, retorne
explicitamente Result.Failure com uma mensagem clara (por exemplo "Email
templates not implemented") para que consumidores não ocultem a falta de
funcionalidade; ajuste o método GetTemplatesAsync para depender do repositório e
remover o retorno estático.
🧹 Nitpick comments (3)
src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs (1)

24-27: Evite destinatário administrativo hardcoded

Extrair admin@meajudaai.com.br para configuração (IOptions) evita envio acidental entre ambientes e facilita manutenção operacional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs`
around lines 24 - 27, The handler currently hardcodes the admin recipient
("admin@meajudaai.com.br"); inject a configuration-backed option (e.g.,
IOptions<AdminEmailsOptions> or IConfiguration) into
ProviderAwaitingVerificationIntegrationEventHandler, add a property like
AdminEmail or ProviderVerificationRecipient to that options class, and replace
the hardcoded To value with the configured value (e.g.,
_options.Value.ProviderVerificationRecipient) when constructing the email (the
EmailMessage/To assignment in the handler). Update constructor signature to
accept the options, store it in a private readonly field, and use that field
instead of the literal string so environments can override the admin recipient
via configuration.
tests/MeAjudaAi.Integration.Tests/Modules/Communications/CommunicationsModuleApiTests.cs (1)

45-53: Considere adicionar o cenário 401 para /templates

Como o grupo inteiro exige autenticação, vale incluir um teste equivalente sem autenticação para /api/v1/communications/templates para manter cobertura simétrica.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/Communications/CommunicationsModuleApiTests.cs`
around lines 45 - 53, Add a symmetric unauthenticated test for the templates
endpoint: in the CommunicationsModuleApiTests class, create a new [Fact] async
test named GetTemplates_WithoutAuthentication_ShouldReturnUnauthorized that
mirrors GetLogs_WithoutAuthentication_ShouldReturnUnauthorized by calling
Client.GetAsync("/api/v1/communications/templates") and asserting
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); keep the same test
structure and naming convention as the existing
GetLogs_WithoutAuthentication_ShouldReturnUnauthorized.
tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs (1)

432-433: Padronize o CommunicationsDbContext no setup de teste

Agora que o fluxo resolve e migra CommunicationsDbContext, recomendo registrá-lo explicitamente no ConfigureServices de teste (como os demais contextos) para reduzir drift de configuração entre ambiente real e integração.

Also applies to: 480-485

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs` around lines 432 -
433, Registre explicitamente o CommunicationsDbContext no método
ConfigureServices de teste (na classe BaseApiTest) para padronizar com os outros
DbContexts: adicione a mesma chamada de AddDbContext/AddDbContextPool usada para
os demais contextos, garantindo que o provider resolva CommunicationsDbContext
(referenciado em modules.HasFlag(TestModule.Communications) e
serviceProvider.GetRequiredService<CommunicationsDbContext>), e repita a mesma
mudança onde houver setup similar mencionado nas linhas 480-485 para evitar
drift entre ambiente real e integração.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs`:
- Around line 21-23: O handler
ProviderVerificationStatusUpdatedIntegrationEventHandler está usando um
placeholder ("provider-email-lookup-pending@meajudaai.com.br") em
recipientEmail; em vez disso, resolva o e-mail real do prestador usando o
IUsersModuleApi com integrationEvent.UserId antes de enfileirar a notificação,
tratando erros/nulos (ex.: lançar/registrar ou abortar o envio se não houver
e-mail) e substituir a atribuição do campo recipientEmail pela chamada ao método
do IUsersModuleApi que retorna o e-mail do usuário.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Line 184: O comentário em inglês "// Payload records (serialized into Outbox)"
no arquivo OutboxProcessorService.cs deve ser padronizado para português;
substitua esse comentário por uma versão em português consistente com o estilo
do código (por exemplo "Registros de payload (serializados no Outbox)" ou
semelhante) no local onde aparece na classe OutboxProcessorService para manter a
uniformidade dos comentários na base.
- Around line 57-120: The code currently dispatches external messages via
DispatchMessageAsync and only persists message state/logs at the end
(outboxRepository.SaveChangesAsync/_logRepository.SaveChangesAsync), which can
cause duplicate deliveries if persistence fails; change the flow so that after a
successful DispatchMessageAsync you immediately call message.MarkAsSent(...)
then persist that state and the CommunicationLog by calling
_logRepository.AddAsync(...) and saving both repositories (or, preferably,
enlisting both operations in the same transaction) before proceeding to the next
message, and ensure that exceptions thrown by _logRepository.AddAsync or
SaveChangesAsync do not call message.MarkAsFailed or revert the already-sent
external effect—handle persistence failures separately (retry/save to
dead-letter/store) instead of marking messages failed in the catch blocks that
also wrap dispatch.

---

Duplicate comments:
In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`:
- Around line 32-36: Adicione validações fail-fast em todos os métodos públicos
do módulo (por exemplo SendEmailAsync) para validar imediatamente parâmetros de
entrada: verifique email != null e campos obrigatórios dentro de EmailMessageDto
(destinatários, subject, body), valide que priority é um valor válido do enum
CommunicationPriority, e para métodos de paginação valide page/pageSize > 0 e
limites máximos; ao detectar entrada inválida retorne de imediato um Result de
falha com mensagem clara em vez de enfileirar ou prosseguir. Implemente essas
checagens no início de SendEmailAsync e nas demais APIs públicas listadas no
arquivo (aplique mesma lógica para os métodos aos quais o comentário se refere)
usando os símbolos existentes (SendEmailAsync, EmailMessageDto,
CommunicationPriority) e mantenha comportamento assíncrono/assinatura
(Task<Result<...>>) retornando Result.Failure apropriado.
- Around line 49-54: O método GetTemplatesAsync atualmente devolve sempre um
Success com lista vazia; substitua esse placeholder por uma implementação real:
invoque o repositório de templates (ex.:
_emailTemplateRepository.GetAllAsync(ct) ou adicione um método GetAll/FindByKeys
em IEmailTemplateRepository) e mapeie os resultados para EmailTemplateDto
retornando Result.Success(list) ou, enquanto a persistência não existir, retorne
explicitamente Result.Failure com uma mensagem clara (por exemplo "Email
templates not implemented") para que consumidores não ocultem a falta de
funcionalidade; ajuste o método GetTemplatesAsync para depender do repositório e
remover o retorno estático.

---

Nitpick comments:
In
`@src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs`:
- Around line 24-27: The handler currently hardcodes the admin recipient
("admin@meajudaai.com.br"); inject a configuration-backed option (e.g.,
IOptions<AdminEmailsOptions> or IConfiguration) into
ProviderAwaitingVerificationIntegrationEventHandler, add a property like
AdminEmail or ProviderVerificationRecipient to that options class, and replace
the hardcoded To value with the configured value (e.g.,
_options.Value.ProviderVerificationRecipient) when constructing the email (the
EmailMessage/To assignment in the handler). Update constructor signature to
accept the options, store it in a private readonly field, and use that field
instead of the literal string so environments can override the admin recipient
via configuration.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Around line 432-433: Registre explicitamente o CommunicationsDbContext no
método ConfigureServices de teste (na classe BaseApiTest) para padronizar com os
outros DbContexts: adicione a mesma chamada de AddDbContext/AddDbContextPool
usada para os demais contextos, garantindo que o provider resolva
CommunicationsDbContext (referenciado em
modules.HasFlag(TestModule.Communications) e
serviceProvider.GetRequiredService<CommunicationsDbContext>), e repita a mesma
mudança onde houver setup similar mencionado nas linhas 480-485 para evitar
drift entre ambiente real e integração.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/Communications/CommunicationsModuleApiTests.cs`:
- Around line 45-53: Add a symmetric unauthenticated test for the templates
endpoint: in the CommunicationsModuleApiTests class, create a new [Fact] async
test named GetTemplates_WithoutAuthentication_ShouldReturnUnauthorized that
mirrors GetLogs_WithoutAuthentication_ShouldReturnUnauthorized by calling
Client.GetAsync("/api/v1/communications/templates") and asserting
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); keep the same test
structure and naming convention as the existing
GetLogs_WithoutAuthentication_ShouldReturnUnauthorized.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f2a310a3-3e37-4881-892e-a452b38ffef6

📥 Commits

Reviewing files that changed from the base of the PR and between e61a444 and 509cce1.

📒 Files selected for processing (16)
  • src/Modules/Communications/API/Endpoints/CommunicationsModuleEndpoints.cs
  • src/Modules/Communications/API/Extensions.cs
  • src/Modules/Communications/Application/Extensions.cs
  • src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Domain/Repositories/ICommunicationLogRepository.cs
  • src/Modules/Communications/Domain/Repositories/IOutboxMessageRepository.cs
  • src/Modules/Communications/Infrastructure/Extensions.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs
  • src/Modules/Communications/Infrastructure/Services/CommunicationsOutboxWorker.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Communications/CommunicationsModuleApiTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Communications/OutboxPatternTests.cs
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/Modules/Communications/Infrastructure/Extensions.cs
  • src/Modules/Communications/Application/Extensions.cs
  • src/Modules/Communications/API/Extensions.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs
  • src/Modules/Communications/Domain/Repositories/ICommunicationLogRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs

Comment thread src/Modules/Communications/Application/Services/OutboxProcessorService.cs Outdated
Comment thread src/Modules/Communications/Application/Services/OutboxProcessorService.cs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/Modules/Communications/Application/Services/OutboxProcessorService.cs (1)

200-200: ⚠️ Potential issue | 🟡 Minor

Padronizar comentário para português.

O comentário na linha 200 está em inglês, mas a política do repositório exige que comentários e documentação XML sejam em português.

🔧 Correção proposta
-    // Payload records (serialized into Outbox)
+    // Registros de payload (serializados no Outbox)
     private sealed record EmailOutboxPayload(string To, string Subject, string HtmlBody, string TextBody, string? From = null, string? TemplateKey = null);

Based on learnings, comentários e documentação XML em C# devem ser escritos em português em todo o codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs` at
line 200, Padronize o comentário em
src/Modules/Communications/Application/Services/OutboxProcessorService.cs
alterando o comentário em inglês "Payload records (serialized into Outbox)" para
português; localize o comentário dentro da classe OutboxProcessorService e
substitua por uma versão em português, por exemplo "Registros de payload
(serializados no Outbox)" garantindo que o estilo e tom estejam consistentes com
os demais comentários do repositório.
src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs (1)

107-138: ⚠️ Potential issue | 🟡 Minor

Falta validação de entrada em GetLogsAsync.

O método GetLogsAsync não valida o parâmetro query. Se vier null ou com paginação inválida, pode causar comportamento inesperado.

🛡️ Correção proposta
     public async Task<Result<PagedResult<CommunicationLogDto>>> GetLogsAsync(
         CommunicationLogQuery query,
         CancellationToken ct = default)
     {
+        ArgumentNullException.ThrowIfNull(query);
+        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(query.PageNumber);
+        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(query.PageSize);
+
         var (items, totalCount) = await logRepository.SearchAsync(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`
around lines 107 - 138, O método GetLogsAsync não valida o parâmetro query nem
os campos de paginação; adicione uma validação no início de GetLogsAsync que
verifique se query é nulo e retorne
Result<PagedResult<CommunicationLogDto>>.Failure com uma mensagem de erro
apropriada, e valide query.PageNumber e query.PageSize (por exemplo PageNumber
>= 1 e PageSize entre 1 e um limite razoável como 100) antes de chamar
logRepository.SearchAsync; se os valores de paginação forem inválidos, retorne
Failure indicando o problema. Garanta que as referências usadas na validação
correspondam a CommunicationLogQuery, GetLogsAsync, logRepository.SearchAsync e
ao tipo de retorno Result<PagedResult<CommunicationLogDto>>.
🧹 Nitpick comments (1)
src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs (1)

15-50: Implementação de claim atômico está correta, mas considere recuperação de mensagens "travadas".

A transação em GetPendingAsync resolve o problema de processamento duplicado por workers concorrentes. Porém, se o worker falhar após o commit da transação (linha 42) mas antes de persistir o status final (Sent/Failed), as mensagens ficarão permanentemente no estado Processing e nunca serão reprocessadas.

Recomendo implementar um mecanismo de timeout para recuperar mensagens travadas:

  • Um job periódico que busca mensagens Processing com UpdatedAt < DateTime.UtcNow.AddMinutes(-X)
  • Reverter essas mensagens para Pending para reprocessamento
💡 Exemplo de query para mensagens travadas
public async Task<int> ResetStuckMessagesAsync(TimeSpan timeout, CancellationToken ct = default)
{
    var threshold = DateTime.UtcNow - timeout;
    var stuckMessages = await context.OutboxMessages
        .Where(x => x.Status == EOutboxMessageStatus.Processing && x.UpdatedAt < threshold)
        .ToListAsync(ct);
    
    foreach (var msg in stuckMessages)
        msg.ResetToPending(); // Novo método na entidade
    
    return await context.SaveChangesAsync(ct);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs`
around lines 15 - 50, GetPendingAsync correctly claims messages but can leave
them stuck in Processing if a worker crashes; add a recovery mechanism by
implementing a repository method ResetStuckMessagesAsync(TimeSpan timeout,
CancellationToken ct = default) that computes a threshold = DateTime.UtcNow -
timeout, queries context.OutboxMessages for Status ==
EOutboxMessageStatus.Processing && UpdatedAt < threshold, calls a new entity
method ResetToPending() on each result, and saves changes; wire this into a
periodic background job (cron/hosted service) to run the recovery job at your
desired interval so stuck messages are reverted to Pending for reprocessing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Around line 128-139: Change the ExtractTemplateKey method signature in
OutboxProcessorService to return a nullable string (string?) instead of string
to eliminate CS8603 warnings; update the method declaration for
ExtractTemplateKey(OutboxMessage message) to string? and keep the existing logic
that returns null on non-email channels or deserialization exceptions, and then
review any callers of ExtractTemplateKey to handle the nullable return (use null
checks or the null-coalescing operator) so no new nullable warnings are
introduced; references: ExtractTemplateKey, OutboxProcessorService,
OutboxMessage, EmailOutboxPayload.

---

Duplicate comments:
In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`:
- Around line 107-138: O método GetLogsAsync não valida o parâmetro query nem os
campos de paginação; adicione uma validação no início de GetLogsAsync que
verifique se query é nulo e retorne
Result<PagedResult<CommunicationLogDto>>.Failure com uma mensagem de erro
apropriada, e valide query.PageNumber e query.PageSize (por exemplo PageNumber
>= 1 e PageSize entre 1 e um limite razoável como 100) antes de chamar
logRepository.SearchAsync; se os valores de paginação forem inválidos, retorne
Failure indicando o problema. Garanta que as referências usadas na validação
correspondam a CommunicationLogQuery, GetLogsAsync, logRepository.SearchAsync e
ao tipo de retorno Result<PagedResult<CommunicationLogDto>>.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Line 200: Padronize o comentário em
src/Modules/Communications/Application/Services/OutboxProcessorService.cs
alterando o comentário em inglês "Payload records (serialized into Outbox)" para
português; localize o comentário dentro da classe OutboxProcessorService e
substitua por uma versão em português, por exemplo "Registros de payload
(serializados no Outbox)" garantindo que o estilo e tom estejam consistentes com
os demais comentários do repositório.

---

Nitpick comments:
In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs`:
- Around line 15-50: GetPendingAsync correctly claims messages but can leave
them stuck in Processing if a worker crashes; add a recovery mechanism by
implementing a repository method ResetStuckMessagesAsync(TimeSpan timeout,
CancellationToken ct = default) that computes a threshold = DateTime.UtcNow -
timeout, queries context.OutboxMessages for Status ==
EOutboxMessageStatus.Processing && UpdatedAt < threshold, calls a new entity
method ResetToPending() on each result, and saves changes; wire this into a
periodic background job (cron/hosted service) to run the recovery job at your
desired interval so stuck messages are reverted to Pending for reprocessing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8b4df4bd-263e-4ee1-bf35-f89eea47829d

📥 Commits

Reviewing files that changed from the base of the PR and between 509cce1 and bb422c3.

📒 Files selected for processing (22)
  • docs/roadmap-current.md
  • src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs
  • src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs
  • src/Modules/Communications/Application/Services/Email/StubEmailService.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Domain/Entities/CommunicationLog.cs
  • src/Modules/Communications/Domain/Entities/EmailTemplate.cs
  • src/Modules/Communications/Domain/Entities/OutboxMessage.cs
  • src/Modules/Communications/Domain/Enums/EOutboxMessageStatus.cs
  • src/Modules/Communications/Domain/Repositories/IEmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/CommunicationsDbContext.cs
  • src/Modules/Communications/Infrastructure/Persistence/CommunicationsDbContextFactory.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/EmailTemplateConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs
  • src/Modules/Communications/Infrastructure/Services/EmailSenderStub.cs
  • src/Modules/Communications/Infrastructure/Services/PushSenderStub.cs
  • src/Modules/Communications/Infrastructure/Services/SmsSenderStub.cs
✅ Files skipped from review due to trivial changes (2)
  • src/Modules/Communications/Infrastructure/Services/PushSenderStub.cs
  • src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/Modules/Communications/Infrastructure/Services/SmsSenderStub.cs
  • src/Modules/Communications/Domain/Enums/EOutboxMessageStatus.cs
  • src/Modules/Communications/Infrastructure/Services/EmailSenderStub.cs
  • src/Modules/Communications/Application/Services/Email/StubEmailService.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/EmailTemplateConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs
  • src/Modules/Communications/Domain/Repositories/IEmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs
  • src/Modules/Communications/Domain/Entities/OutboxMessage.cs

Comment thread src/Modules/Communications/Application/Services/OutboxProcessorService.cs Outdated
Filipe Frigini added 3 commits April 8, 2026 21:47
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (1)
docs/roadmap-current.md (1)

659-659: ⚠️ Potential issue | 🟡 Minor

Padronize terminologia PT-BR e corrija pequenos pontos de texto para reduzir ruído no roadmap.

Há mistura de termos (templates, Follow-ups, Evento Existing) e um acento faltando (facil), o que reduz consistência editorial.

✏️ Sugestão objetiva
- - [ ] **Módulo Comunicações**: Implementar infraestrutura base (outbox pattern, templates, event handlers)
+ - [ ] **Módulo Comunicações**: Implementar infraestrutura base (outbox pattern, modelos, event handlers)

- | **Unitários** | Lógica de templates, cálculo de retries, mapeamento de DTOs | xUnit + FluentAssertions |
+ | **Unitários** | Lógica de modelos, cálculo de retries, mapeamento de DTOs | xUnit + FluentAssertions |

- > **📝 Decisão Técnica** (8 Abr 2026): ... permitindo troca facil.
+ > **📝 Decisão Técnica** (8 Abr 2026): ... permitindo troca fácil.

- **Localização de Templates**:
+ **Localização de Modelos**:

- | Evento Existing | Ação de Comunicação |
+ | Evento existente | Ação de Comunicação |

- | 8. Criar templates básicos (.cshtml) | 3h | - |
+ | 8. Criar modelos básicos (.cshtml) | 3h | - |

- ✅ Sistema de templates híbrido funcional
+ ✅ Sistema de modelos híbrido funcional

- **Follow-ups Pendentes**:
+ **Acompanhamentos Pendentes**:

- - [ ] Admin UI para gestão dinâmica de templates
+ - [ ] Admin UI para gestão dinâmica de modelos

Also applies to: 798-798, 815-815, 868-871, 877-877, 991-991, 1002-1002, 1009-1011

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` at line 659, Resumo: padronizar terminologia PT-BR e
corrigir acentuação no roadmap. In the docs line containing "Módulo
Comunicações" replace English terms with Portuguese equivalents (e.g., change
"templates" → "modelos", "Follow-ups" → "acompanhamentos" or "seguimentos" where
context fits, and "Evento Existing" → "Evento existente"), and fix the missing
accent "facil" → "fácil"; apply the same translations/orthography fixes to the
other referenced occurrences ("templates", "Follow-ups", "Evento Existing",
"facil") across the document so terminology is consistent.
🧹 Nitpick comments (8)
src/Shared/Utilities/PiiMaskingHelper.cs (1)

54-59: Resumo XML de MaskSensitiveData não condiz com a implementação.

As Lines 54-55 indicam preservação de tamanho/prefixo, mas a função (Line 59) sempre retorna "[REDACTED]". Recomendo alinhar a documentação ao comportamento atual para evitar uso incorreto por outros módulos.

📝 Ajuste sugerido de documentação
 /// <summary>
-/// Mascara um dado sensível genérico mantendo apenas o tamanho ou prefixo.
+/// Mascara um dado sensível genérico com redação total.
 /// </summary>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Utilities/PiiMaskingHelper.cs` around lines 54 - 59, The XML
summary for MaskSensitiveData is incorrect: it claims the method preserves
size/prefix but the implementation always returns "[REDACTED]" for non-empty
input and "[EMPTY]" for null/whitespace. Fix by updating the method's XML doc
comment (the /// summary for MaskSensitiveData) to accurately describe the
current behavior—state that it returns "[EMPTY]" when input is null/whitespace
and otherwise returns a constant "[REDACTED]"; alternatively, if you prefer the
original behavior, modify the MaskSensitiveData implementation to preserve
length/prefix as documented—choose one consistent change so doc and
implementation match.
docs/roadmap-current.md (1)

887-912: Reforce explicitamente a fonte canônica dos contratos para evitar drift.

No bloco da ICommunicationsModuleApi, vale anotar que enums/DTOs devem vir de MeAjudaAi.Shared.Contracts (ou contrato compartilhado equivalente), para manter Web/backend sincronizados.

Based on learnings: prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 887 - 912, Atualize a documentação da
interface ICommunicationsModuleApi para explicitar que todos os tipos e enums
usados (por exemplo CommunicationPriority, EmailMessageDto, SmsMessageDto,
PushMessageDto, CommunicationLogDto, CommunicationLogQuery, PagedResult) devem
ser importados do contrato canônico MeAjudaAi.Shared.Contracts (ou do namespace
de contrato compartilhado equivalente); adicione uma frase clara junto à
declaração da interface indicando "Usar tipos de MeAjudaAi.Shared.Contracts" e,
quando pertinente, troque menções locais de enums/DTOs por referências ao
contrato compartilhado para evitar drift entre backend e frontend.
tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs (2)

17-19: Padronize comentários inline para português neste arquivo C#.

Os comentários nesses trechos estão em inglês; vale alinhar ao padrão do repositório para manter consistência.

Based on learnings: em C#, comentários/XML devem estar em português, mantendo logs em inglês.

Also applies to: 46-46, 69-69

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs`
around lines 17 - 19, Padronize os comentários neste arquivo
SearchProvidersE2ETests.cs para português: substitua os comentários inline em
inglês (ex.: o bloco de comentários na área de "Arrange: Seed providers at
specific coordinates" nas proximidades de SearchProvidersE2ETests, e os
comentários nas posições referenciadas por 46 e 69) por equivalentes em
português mantendo o mesmo sentido; preserve mensagens de log em inglês conforme
a orientação e verifique também comentários XML/documentação (se houver) no
mesmo arquivo para tradução consistente.

62-63: Garanta DistanceInKm preenchido antes de validar ordenação.

A ordenação atual usa chave nullable. Se vier null, o teste pode não detectar regressão no cálculo de distância.

💡 Ajuste sugerido
-        var result = await response.Content.ReadFromJsonAsync<PagedResult<SearchableProviderDto>>();
-        result!.Items.Should().BeInAscendingOrder(x => x.DistanceInKm);
+        var result = await response.Content.ReadFromJsonAsync<PagedResult<SearchableProviderDto>>();
+        result.Should().NotBeNull();
+        result!.Items.Should().OnlyContain(x => x.DistanceInKm.HasValue);
+        result.Items.Should().BeInAscendingOrder(x => x.DistanceInKm!.Value);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs`
around lines 62 - 63, Before asserting ordering on result!.Items by
DistanceInKm, ensure DistanceInKm is populated for every SearchableProviderDto:
verify no nulls (e.g., assert Items.All(x => x.DistanceInKm.HasValue)) or fail
the test if any DistanceInKm is null so the sort assertion on
result!.Items.Should().BeInAscendingOrder(x => x.DistanceInKm) is meaningful;
reference the PagedResult<SearchableProviderDto> result and the Items collection
and add a precondition check for DistanceInKm to guarantee the test detects
regressions in distance calculation.
src/Modules/Communications/Application/Services/OutboxProcessorService.cs (1)

35-35: Campo _logRepository redundante.

O primary constructor já captura logRepository como parâmetro. A reatribuição para _logRepository é desnecessária — as outras dependências (outboxRepository, emailSender, etc.) são usadas diretamente sem reatribuição.

♻️ Correção proposta
 internal sealed class OutboxProcessorService(
     IOutboxMessageRepository outboxRepository,
     ICommunicationLogRepository logRepository,
     IEmailSender emailSender,
     ISmsSender smsSender,
     IPushSender pushSender,
     ILogger<OutboxProcessorService> logger) : IOutboxProcessorService
 {
-    private readonly ICommunicationLogRepository _logRepository = logRepository;
-
     public async Task<int> ProcessPendingMessagesAsync(

E substituir _logRepository por logRepository nas linhas 95, 120 e 121.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs` at
line 35, Remove the redundant re-assignment field `_logRepository =
logRepository`; instead use the injected constructor parameter `logRepository`
directly. Delete the unnecessary field declaration `_logRepository` and update
all usages of `_log_repository` (e.g., in methods referencing `_logRepository`)
to `logRepository` (the constructor parameter) so the service relies on the
injected dependency without reassigning it.
src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs (2)

42-56: Considere adicionar parâmetro priority aos métodos SMS e Push para consistência.

SendEmailAsync aceita CommunicationPriority, mas SendSmsAsync e SendPushAsync não. Isso pode limitar a flexibilidade de priorização para outros canais.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs`
around lines 42 - 56, Add an optional CommunicationPriority parameter to
SendSmsAsync and SendPushAsync to match SendEmailAsync: update the method
signatures for SendSmsAsync(SmsMessageDto sms, CommunicationPriority priority =
CommunicationPriority.Normal, CancellationToken ct = default) and
SendPushAsync(PushMessageDto push, CommunicationPriority priority =
CommunicationPriority.Normal, CancellationToken ct = default), update their XML
docs to document the new priority parameter and default, and ensure any
implementing interfaces/classes that implement these methods (and any callers)
are updated to accept/forward the new priority value; reference SendEmailAsync,
CommunicationPriority, SmsMessageDto, PushMessageDto, SendSmsAsync, and
SendPushAsync when locating the change.

11-16: Centralize CommunicationPriority em Shared.Contracts.

Existe duplicação de enums: CommunicationPriority (Contracts) e ECommunicationPriority (Domain) com valores idênticos (Low=0, Normal=1, High=2). Atualmente há mapeamento explícito (MapPriority() em CommunicationsModuleApi.cs) que converte entre eles. Mova o enum para Shared.Contracts conforme padrão do repositório, eliminando a conversão desnecessária e mantendo API contracts alinhados com domínio.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs`
around lines 11 - 16, Há duplicação do enum (CommunicationPriority em Contracts
e ECommunicationPriority em Domain); mova a definição única para
Shared.Contracts, remova a outra e elimine a conversão em MapPriority.
Concretamente: cutar/remover the enum CommunicationPriority from
ICommunicationsModuleApi.cs and the enum ECommunicationPriority from the domain,
add a single enum CommunicationPriority in the Shared.Contracts
project/namespace, update all references to use
Shared.Contracts.CommunicationPriority (including usages in
CommunicationsModuleApi.cs and domain code) and delete the MapPriority() method
in CommunicationsModuleApi.cs (or replace its callers to use the shared enum
directly). Certifique-se de ajustar namespaces/imports and run build to resolve
any remaining references.
src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs (1)

45-54: Reduzir duplicação no fluxo de enfileiramento do Outbox

Os três métodos de envio repetem o mesmo padrão (serializar payload → criar OutboxMessageAddAsyncSaveChangesAsync). Vale extrair um helper privado para diminuir duplicação e risco de divergência futura.

Also applies to: 79-88, 98-107

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`
around lines 45 - 54, Resumo: reduzir duplicação extraindo o padrão de
enfileiramento do Outbox para um helper privado. Crie um método privado como
EnqueueOutboxAsync<TPayload>(ECommunicationChannel channel, TPayload payloadObj,
PriorityType priority, CancellationToken ct) que faça
JsonSerializer.Serialize(payloadObj), chame OutboxMessage.Create(channel,
serializedPayload, MapPriority(priority)), invoque
outboxRepository.AddAsync(...) e outboxRepository.SaveChangesAsync(...), e
retorne Result<Guid>.Substitua os três blocos de
serialização/criação/AddAsync/SaveChangesAsync nas operações de envio para
delegarem a esse helper, mantendo MapPriority, OutboxMessage.Create,
outboxRepository.AddAsync e SaveChangesAsync como os pontos de integração.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`:
- Around line 37-40: Padronize as mensagens de validação para português trocando
as strings passadas para Error.BadRequest(...) na classe CommunicationsModuleApi
(os chamados onde se valida email == null, email.To, email.Subject, email.Body e
os outros Error.BadRequest apontados nas ranges mencionadas) por mensagens em
pt-BR concisas e consistentes (ex.: "Mensagem de email não pode ser nula.",
"Email do destinatário é obrigatório.", "Assunto do email é obrigatório.",
"Corpo do email é obrigatório."), aplicando a mesma padronização nos demais
Error.BadRequest encontrados nas outras verificações da mesma classe (as
ocorrências em torno das linhas 43, 75, 93 e 114).

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Around line 211-214: Os handlers
ProviderAwaitingVerificationIntegrationEventHandler e
ProviderVerificationStatusUpdatedIntegrationEventHandler estão serializando o
email payload com um campo Body enquanto o record EmailOutboxPayload define
HtmlBody e TextBody, causando desserialização nula; para corrigir, alinhe os
formatos: ou atualize EmailOutboxPayload (o record EmailOutboxPayload) para
aceitar um único Body opcional (por exemplo Body) e manter compatibilidade com
From/TemplateKey, ou altere os dois handlers para serializar HtmlBody/TextBody
em vez de Body; ajuste também qualquer código que desserialize/consuma o payload
(onde o payload é desserializado antes do envio de email) para usar os nomes de
campo atualizados para evitar valores nulos no envio de email.

In `@src/Modules/Communications/Domain/Entities/OutboxMessage.cs`:
- Around line 145-161: MarkAsFailed currently leaves Status as Processing when
retries remain, preventing requeue; update the method (MarkAsFailed) to set
Status = EOutboxMessageStatus.Pending whenever RetryCount is incremented and
RetryCount < MaxRetries (while still setting ErrorMessage, calling MarkAsUpdated
and only setting Status = EOutboxMessageStatus.Failed when RetryCount >=
MaxRetries), referencing the RetryCount, MaxRetries, Status and
EOutboxMessageStatus symbols so the message becomes eligible for
GetPendingAsync.

In `@src/Shared/Utilities/PiiMaskingHelper.cs`:
- Around line 45-50: MaskPhoneNumber currently concatenates a 5-char prefix and
a 4-char suffix for short inputs which can overlap and leak PII; update
MaskPhoneNumber to handle short lengths explicitly: keep the existing
null/whitespace and very-short (<=4) handling, add a branch for
phoneNumber.Length between 5 and 8 that returns a fully masked prefix with only
the last 2 characters shown, and only use the current
$"{phoneNumber[..5]}****{phoneNumber[^4..]}" form when phoneNumber.Length is
sufficiently long (e.g., >8) to avoid overlap; implement these checks inside the
MaskPhoneNumber method.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs`:
- Around line 54-64: Add explicit HTTP status assertions before calling
ReadFromJsonAsync in the tests: in Search_ShouldBeOrderedByDistanceAscending
(and the other test around lines 83-95), assert response.IsSuccessStatusCode or
check response.StatusCode == HttpStatusCode.OK immediately after awaiting
Client.GetAsync, then proceed to ReadFromJsonAsync and the existing
FluentAssertions on result.Items; update test method names
Search_ShouldBeOrderedByDistanceAscending (and the corresponding second test) to
include this pre-deserialization status check so failures show clear HTTP error
codes.
- Around line 17-32: In SearchProvidersE2ETests, the test currently accepts any
non-null payload and omits verifying actual nearby results; restore a strict
assertion by asserting result.Items is not empty (uncomment or add
result.Items.Should().NotBeEmpty()) and additionally validate each
SearchableProviderDto in result.Items has a location within the requested radius
(use the DTO's latitude/longitude fields and a simple haversine or distance
check) after calling Client.GetAsync; reference
PagedResult<SearchableProviderDto>, result.Items and Client.GetAsync to find
where to add these assertions.

---

Duplicate comments:
In `@docs/roadmap-current.md`:
- Line 659: Resumo: padronizar terminologia PT-BR e corrigir acentuação no
roadmap. In the docs line containing "Módulo Comunicações" replace English terms
with Portuguese equivalents (e.g., change "templates" → "modelos", "Follow-ups"
→ "acompanhamentos" or "seguimentos" where context fits, and "Evento Existing" →
"Evento existente"), and fix the missing accent "facil" → "fácil"; apply the
same translations/orthography fixes to the other referenced occurrences
("templates", "Follow-ups", "Evento Existing", "facil") across the document so
terminology is consistent.

---

Nitpick comments:
In `@docs/roadmap-current.md`:
- Around line 887-912: Atualize a documentação da interface
ICommunicationsModuleApi para explicitar que todos os tipos e enums usados (por
exemplo CommunicationPriority, EmailMessageDto, SmsMessageDto, PushMessageDto,
CommunicationLogDto, CommunicationLogQuery, PagedResult) devem ser importados do
contrato canônico MeAjudaAi.Shared.Contracts (ou do namespace de contrato
compartilhado equivalente); adicione uma frase clara junto à declaração da
interface indicando "Usar tipos de MeAjudaAi.Shared.Contracts" e, quando
pertinente, troque menções locais de enums/DTOs por referências ao contrato
compartilhado para evitar drift entre backend e frontend.

In `@src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs`:
- Around line 42-56: Add an optional CommunicationPriority parameter to
SendSmsAsync and SendPushAsync to match SendEmailAsync: update the method
signatures for SendSmsAsync(SmsMessageDto sms, CommunicationPriority priority =
CommunicationPriority.Normal, CancellationToken ct = default) and
SendPushAsync(PushMessageDto push, CommunicationPriority priority =
CommunicationPriority.Normal, CancellationToken ct = default), update their XML
docs to document the new priority parameter and default, and ensure any
implementing interfaces/classes that implement these methods (and any callers)
are updated to accept/forward the new priority value; reference SendEmailAsync,
CommunicationPriority, SmsMessageDto, PushMessageDto, SendSmsAsync, and
SendPushAsync when locating the change.
- Around line 11-16: Há duplicação do enum (CommunicationPriority em Contracts e
ECommunicationPriority em Domain); mova a definição única para Shared.Contracts,
remova a outra e elimine a conversão em MapPriority. Concretamente:
cutar/remover the enum CommunicationPriority from ICommunicationsModuleApi.cs
and the enum ECommunicationPriority from the domain, add a single enum
CommunicationPriority in the Shared.Contracts project/namespace, update all
references to use Shared.Contracts.CommunicationPriority (including usages in
CommunicationsModuleApi.cs and domain code) and delete the MapPriority() method
in CommunicationsModuleApi.cs (or replace its callers to use the shared enum
directly). Certifique-se de ajustar namespaces/imports and run build to resolve
any remaining references.

In `@src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs`:
- Around line 45-54: Resumo: reduzir duplicação extraindo o padrão de
enfileiramento do Outbox para um helper privado. Crie um método privado como
EnqueueOutboxAsync<TPayload>(ECommunicationChannel channel, TPayload payloadObj,
PriorityType priority, CancellationToken ct) que faça
JsonSerializer.Serialize(payloadObj), chame OutboxMessage.Create(channel,
serializedPayload, MapPriority(priority)), invoque
outboxRepository.AddAsync(...) e outboxRepository.SaveChangesAsync(...), e
retorne Result<Guid>.Substitua os três blocos de
serialização/criação/AddAsync/SaveChangesAsync nas operações de envio para
delegarem a esse helper, mantendo MapPriority, OutboxMessage.Create,
outboxRepository.AddAsync e SaveChangesAsync como os pontos de integração.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Line 35: Remove the redundant re-assignment field `_logRepository =
logRepository`; instead use the injected constructor parameter `logRepository`
directly. Delete the unnecessary field declaration `_logRepository` and update
all usages of `_log_repository` (e.g., in methods referencing `_logRepository`)
to `logRepository` (the constructor parameter) so the service relies on the
injected dependency without reassigning it.

In `@src/Shared/Utilities/PiiMaskingHelper.cs`:
- Around line 54-59: The XML summary for MaskSensitiveData is incorrect: it
claims the method preserves size/prefix but the implementation always returns
"[REDACTED]" for non-empty input and "[EMPTY]" for null/whitespace. Fix by
updating the method's XML doc comment (the /// summary for MaskSensitiveData) to
accurately describe the current behavior—state that it returns "[EMPTY]" when
input is null/whitespace and otherwise returns a constant "[REDACTED]";
alternatively, if you prefer the original behavior, modify the MaskSensitiveData
implementation to preserve length/prefix as documented—choose one consistent
change so doc and implementation match.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs`:
- Around line 17-19: Padronize os comentários neste arquivo
SearchProvidersE2ETests.cs para português: substitua os comentários inline em
inglês (ex.: o bloco de comentários na área de "Arrange: Seed providers at
specific coordinates" nas proximidades de SearchProvidersE2ETests, e os
comentários nas posições referenciadas por 46 e 69) por equivalentes em
português mantendo o mesmo sentido; preserve mensagens de log em inglês conforme
a orientação e verifique também comentários XML/documentação (se houver) no
mesmo arquivo para tradução consistente.
- Around line 62-63: Before asserting ordering on result!.Items by DistanceInKm,
ensure DistanceInKm is populated for every SearchableProviderDto: verify no
nulls (e.g., assert Items.All(x => x.DistanceInKm.HasValue)) or fail the test if
any DistanceInKm is null so the sort assertion on
result!.Items.Should().BeInAscendingOrder(x => x.DistanceInKm) is meaningful;
reference the PagedResult<SearchableProviderDto> result and the Items collection
and add a precondition check for DistanceInKm to guarantee the test detects
regressions in distance calculation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3b5319b9-fc8c-4e2f-942a-650e77d3bfff

📥 Commits

Reviewing files that changed from the base of the PR and between 509cce1 and 710f48f.

⛔ Files ignored due to path filters (3)
  • src/Modules/Communications/Infrastructure/Persistence/Migrations/20260409004631_AddCorrelationIdToOutbox.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/Communications/Infrastructure/Persistence/Migrations/20260409004631_AddCorrelationIdToOutbox.cs is excluded by !**/Migrations/**
  • src/Modules/Communications/Infrastructure/Persistence/Migrations/CommunicationsDbContextModelSnapshot.cs is excluded by !**/Migrations/**
📒 Files selected for processing (31)
  • docs/roadmap-current.md
  • docs/technical-debt.md
  • src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs
  • src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs
  • src/Modules/Communications/Application/Services/Email/StubEmailService.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Domain/Entities/CommunicationLog.cs
  • src/Modules/Communications/Domain/Entities/EmailTemplate.cs
  • src/Modules/Communications/Domain/Entities/OutboxMessage.cs
  • src/Modules/Communications/Domain/Enums/EOutboxMessageStatus.cs
  • src/Modules/Communications/Domain/Repositories/IEmailTemplateRepository.cs
  • src/Modules/Communications/Domain/Repositories/IOutboxMessageRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/CommunicationsDbContext.cs
  • src/Modules/Communications/Infrastructure/Persistence/CommunicationsDbContextFactory.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/EmailTemplateConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/OutboxMessageRepository.cs
  • src/Modules/Communications/Infrastructure/Services/CommunicationsOutboxWorker.cs
  • src/Modules/Communications/Infrastructure/Services/EmailSenderStub.cs
  • src/Modules/Communications/Infrastructure/Services/PushSenderStub.cs
  • src/Modules/Communications/Infrastructure/Services/SmsSenderStub.cs
  • src/Shared/Utilities/PiiMaskingHelper.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Communications/CommunicationsModuleApiTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
✅ Files skipped from review due to trivial changes (9)
  • src/Modules/Communications/Domain/Enums/EOutboxMessageStatus.cs
  • src/Modules/Communications/Infrastructure/Services/PushSenderStub.cs
  • src/Modules/Communications/Application/Services/Email/StubEmailService.cs
  • docs/technical-debt.md
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs
  • src/Modules/Communications/Domain/Repositories/IEmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/EmailTemplateConfiguration.cs
  • src/Modules/Communications/Infrastructure/Services/EmailSenderStub.cs
  • src/Modules/Communications/Domain/Entities/CommunicationLog.cs
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/Modules/Communications/Infrastructure/Persistence/CommunicationsDbContextFactory.cs
  • src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Communications/CommunicationsModuleApiTests.cs
  • src/Modules/Communications/Domain/Repositories/IOutboxMessageRepository.cs
  • src/Modules/Communications/Infrastructure/Services/CommunicationsOutboxWorker.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/CommunicationLogRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs

Comment thread src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs Outdated
Comment thread src/Modules/Communications/Application/Services/OutboxProcessorService.cs Outdated
Comment thread src/Modules/Communications/Domain/Entities/OutboxMessage.cs Outdated
Comment thread src/Shared/Utilities/PiiMaskingHelper.cs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
docs/roadmap-current.md (1)

777-777: ⚠️ Potential issue | 🟡 Minor

Atualize o item de observabilidade para a stack atual.

Na Line 777, o texto ainda aponta para “Blazor WASM”, mas o próprio roadmap já consolidou frontend em React + Next.js. Isso pode desalinhar backlog e execução.

✏️ Ajuste sugerido
-- [ ] **Registro do Frontend**: Integração com Application Insights (Blazor WASM)
+- [ ] **Registro do Frontend**: Integração com Application Insights (React + Next.js)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` at line 777, Atualize o item de observabilidade que
ainda menciona “Blazor WASM”: localize a linha contendo the text "Registro do
Frontend: Integração com Application Insights (Blazor WASM)" e altere para
refletir a stack atual, por exemplo "Registro do Frontend: Integração com
Application Insights (React + Next.js)", garantindo consistência do roadmap com
o frontend consolidado.
🧹 Nitpick comments (2)
src/Modules/Communications/Domain/Entities/OutboxMessage.cs (1)

146-168: Chamada duplicada a MarkAsUpdated().

O método MarkAsUpdated() é chamado duas vezes: na linha 155 (antes da verificação de retries) e na linha 167 (após a atribuição de status). Isso é redundante e pode ser simplificado.

♻️ Sugestão de refatoração
     public void MarkAsFailed(string errorMessage)
     {
         if (Status is EOutboxMessageStatus.Sent or EOutboxMessageStatus.Failed)
             return;

         ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage);

         RetryCount++;
         ErrorMessage = errorMessage;
-        MarkAsUpdated();

         if (RetryCount >= MaxRetries)
         {
             Status = EOutboxMessageStatus.Failed;
         }
         else
         {
             // Reseta para Pending para permitir que seja capturada novamente pelo polling
             Status = EOutboxMessageStatus.Pending;
         }
-        
+
         MarkAsUpdated();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Domain/Entities/OutboxMessage.cs` around lines 146
- 168, O método MarkAsFailed tem uma chamada duplicada a MarkAsUpdated(); remova
a chamada anterior à verificação de RetryCount (a que ocorre logo após atribuir
ErrorMessage e incrementar RetryCount) e deixe apenas uma chamada única a
MarkAsUpdated() depois de definir o Status (uso das propriedades RetryCount,
MaxRetries, ErrorMessage e Status dentro de MarkAsFailed), garantindo que
ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage) permaneça antes do
incremento de RetryCount.
docs/roadmap-current.md (1)

1302-1340: Exemplo técnico está acoplado a Blazor em uma trilha React.

As Lines 1317+ usam sintaxe/componentes de Blazor (@inject, MudTimeline). Recomendo trocar por pseudocódigo agnóstico ou exemplo React para evitar drift arquitetural no roadmap.

♻️ Ajuste sugerido (pseudocódigo agnóstico)
-// Componente Frontend
-@inject HubConnection HubConnection
-
-<MudTimeline>
-    `@foreach` (var activity in RecentActivities)
-    {
-        <MudTimelineItem Color="@GetActivityColor(activity.Type)">
-            <MudText>@activity.Description</MudText>
-            <MudText Typo="Typo.caption">@activity.Timestamp.ToRelativeTime()</MudText>
-        </MudTimelineItem>
-    }
-</MudTimeline>
-
-@code {
-    protected override async Task OnInitializedAsync()
-    {
-        HubConnection.On<ActivityDto>("ReceiveActivity", activity =>
-        {
-            RecentActivities.Insert(0, activity);
-            StateHasChanged();
-        });
-        await HubConnection.StartAsync();
-    }
-}
+// Componente Frontend (React - exemplo conceitual)
+// useEffect(() => subscribeToActivityFeed(...), [])
+// renderTimeline(activities)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 1302 - 1340, The example in the docs
mixes Blazor-specific syntax/components (e.g., `@inject` HubConnection,
MudTimeline, MudTimelineItem, OnInitializedAsync) while the roadmap track is
React; replace the Blazor fragment with either framework-agnostic pseudocode or
a small React example using the same domain events and SignalR hub (ActivityHub
and its BroadcastActivity method) so readers in the React track see a consistent
example; update the client snippet to show subscribing to "ReceiveActivity" on a
HubConnection (or equivalent pseudocode) and updating a RecentActivities
array/state so the mapping between
ProviderRegisteredEvent/DocumentUploadedEvent/VerificationStatusChangedEvent →
ActivityDto is clear without Blazor-specific markup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs`:
- Around line 60-61: Replace the plain recipientEmail in the LogInformation call
inside ProviderVerificationStatusUpdatedIntegrationEventHandler with a masked
value: add "using MeAjudaAi.Shared.Utilities;", compute a maskedEmail (e.g., var
maskedEmail = EmailMasker.Mask(recipientEmail) or the repository's email-masking
helper) and pass maskedEmail to logger.LogInformation instead of recipientEmail;
keep the rest of the template (integrationEvent.UserId, correlationId) and
ensure the log message text remains in English.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Around line 187-191: The TestModule flag handling for SearchProviders misses
implicitly adding Locations, causing LocationsDbContext (used in the seeding at
the block that opens LocationsDbContext around the seeding lines) to run without
migrations; update the conditional that checks
modules.HasFlag(TestModule.SearchProviders) to also set modules |=
TestModule.Locations if not already set (in the same style as Providers and
ServiceCatalogs) so SearchProviders brings Locations as a dependency.
- Around line 276-282: The retry loop in ApplyMigrationForContextAsync swallows
failures when all 3 attempts fail; modify the method so that after the for loop
completes it throws an exception (preserving the last caught exception or
wrapping it) to surface migration failure; specifically update
ApplyMigrationForContextAsync to capture the last Npgsql.PostgresException from
the catch around context.Database.MigrateAsync and, if retries are exhausted,
rethrow or throw a new informative exception that includes the original
exception details so test startup fails instead of silently continuing.

---

Duplicate comments:
In `@docs/roadmap-current.md`:
- Line 777: Atualize o item de observabilidade que ainda menciona “Blazor WASM”:
localize a linha contendo the text "Registro do Frontend: Integração com
Application Insights (Blazor WASM)" e altere para refletir a stack atual, por
exemplo "Registro do Frontend: Integração com Application Insights (React +
Next.js)", garantindo consistência do roadmap com o frontend consolidado.

---

Nitpick comments:
In `@docs/roadmap-current.md`:
- Around line 1302-1340: The example in the docs mixes Blazor-specific
syntax/components (e.g., `@inject` HubConnection, MudTimeline, MudTimelineItem,
OnInitializedAsync) while the roadmap track is React; replace the Blazor
fragment with either framework-agnostic pseudocode or a small React example
using the same domain events and SignalR hub (ActivityHub and its
BroadcastActivity method) so readers in the React track see a consistent
example; update the client snippet to show subscribing to "ReceiveActivity" on a
HubConnection (or equivalent pseudocode) and updating a RecentActivities
array/state so the mapping between
ProviderRegisteredEvent/DocumentUploadedEvent/VerificationStatusChangedEvent →
ActivityDto is clear without Blazor-specific markup.

In `@src/Modules/Communications/Domain/Entities/OutboxMessage.cs`:
- Around line 146-168: O método MarkAsFailed tem uma chamada duplicada a
MarkAsUpdated(); remova a chamada anterior à verificação de RetryCount (a que
ocorre logo após atribuir ErrorMessage e incrementar RetryCount) e deixe apenas
uma chamada única a MarkAsUpdated() depois de definir o Status (uso das
propriedades RetryCount, MaxRetries, ErrorMessage e Status dentro de
MarkAsFailed), garantindo que
ArgumentException.ThrowIfNullOrWhiteSpace(errorMessage) permaneça antes do
incremento de RetryCount.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d819f846-a1cb-4e6f-853b-81eafedf38fb

📥 Commits

Reviewing files that changed from the base of the PR and between 710f48f and 0677892.

📒 Files selected for processing (14)
  • docs/roadmap-current.md
  • src/Contracts/Contracts/Modules/Communications/ICommunicationsModuleApi.cs
  • src/Contracts/Shared/ECommunicationPriority.cs
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs
  • src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Domain/Entities/OutboxMessage.cs
  • src/Shared/Utilities/PiiMaskingHelper.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Communications/OutboxPatternTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
✅ Files skipped from review due to trivial changes (2)
  • src/Contracts/Shared/ECommunicationPriority.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/Communications/OutboxPatternTests.cs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
  • src/Modules/Communications/Application/ModuleApi/CommunicationsModuleApi.cs

Comment thread tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
Comment thread tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs (1)

9-9: Remova JsonStringEnumConverter do enum de Domínio

A linha 9 acopla desnecessariamente a camada Domain ao System.Text.Json. O Domain deve manter a política de serialização separada — apenas o Contract (src/Contracts/Contracts/Modules/SearchProviders/Enums/ESubscriptionTier.cs) serializa como número, e a conversão entre os dois tipos ocorre explicitamente em SearchProvidersModuleApi.cs (linhas 250–269).

Remova o atributo desnecessário
- [JsonConverter(typeof(JsonStringEnumConverter))]
  public enum ESubscriptionTier

Isto alinha-se com a prática de reutilizar Shared.Contracts para enums/constantes, mantendo a Web sincronizada com o código do backend.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs` at line 9,
Remove the System.Text.Json attribute from the domain enum: delete the
[JsonConverter(typeof(JsonStringEnumConverter))] annotation on ESubscriptionTier
in the Domain enum file so the Domain layer is not coupled to System.Text.Json;
keep serialization policy in the Contracts enum
(src/Contracts/.../ESubscriptionTier.cs) and rely on the existing explicit
mapping in SearchProvidersModuleApi (methods around SearchProvidersModuleApi.cs
lines ~250–269) to convert between the Contract and Domain enum types.
tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs (1)

277-301: O tratamento de lastException resolve a questão da revisão anterior. Considere restringir retentativas a erros transientes.

A correção que captura e propaga a exceção após esgotar as tentativas está correta. No entanto, o bloco catch (Exception ex) nas linhas 293-297 faz retry para qualquer exceção, incluindo erros não-transientes como conflitos de schema ou migrações inválidas, que não serão resolvidos com retentativas.

♻️ Sugestão: limitar retries a exceções transientes
             catch (Npgsql.PostgresException ex) when (ex.SqlState == "57P01") 
             { 
                 lastException = ex;
                 await Task.Delay(1000 * attempt); 
             }
-            catch (Exception ex)
+            catch (Npgsql.NpgsqlException ex) when (ex.IsTransient)
             {
                 lastException = ex;
-                logger?.LogError(ex, "❌ Failed to apply {Module} migrations on attempt {Attempt}", moduleName, attempt);
+                logger?.LogWarning(ex, "⚠️ Transient error applying {Module} migrations on attempt {Attempt}, retrying...", moduleName, attempt);
+                await Task.Delay(1000 * attempt);
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs` around lines 277 -
301, The retry loop in ApplyMigrationForContextAsync currently retries on any
Exception; change it so only transient errors trigger retries: keep the existing
catch (Npgsql.PostgresException ex) when (ex.SqlState == "57P01") for that
transient case, add explicit transient checks (e.g., Postgres transient SqlState
codes like "57P01","53300","08006" or host/timeout-related exceptions, and
transient exception types such as TimeoutException or transient
DbUpdateException) and only await Task.Delay/continue the loop for those; for
all other exceptions (caught by catch (Exception ex)) immediately log via
logger?.LogError(ex, ...) and rethrow (or set lastException and break) so
non-transient errors do not cause retries, preserving the final throw of
InvalidOperationException with lastException if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/roadmap-current.md`:
- Around line 649-653: The Sprint 9 header "### ⏳ Sprint 9 - BUFFER & Mitigação
de Risco (25 Mar - 11 Mai 2026)" shows a date range that does not match the
shown "Duração: 12 dias de buffer"; update the document so the two are
consistent by either changing the "Duração" value to the actual number of days
covered by 25 Mar–11 Mai 2026 (replace "Duração: 12 dias de buffer" with the
correct total days) or by adjusting the parenthetical date range in the "### ⏳
Sprint 9 - BUFFER & Mitigação de Risco (...)" header to a 12-day interval;
ensure you edit the exact strings "### ⏳ Sprint 9 - BUFFER & Mitigação de Risco
(25 Mar - 11 Mai 2026)" and "Duração: 12 dias de buffer" so they match.
- Around line 867-871: The docs claim IsSystemTemplate on the EmailTemplate
entity prevents deletion but the domain class EmailTemplate only defines the
flag without enforcement; either update the roadmap text to mark this as a
"planned" rule or implement enforcement in the application layer by checking
EmailTemplate.IsSystemTemplate before deleting—e.g., add a guard in
EmailTemplateService.DeleteTemplateAsync (or in the repository method
DeleteAsync) to throw a DomainException/ValidationException or return a 403 when
IsSystemTemplate==true, and update the docs to reference the enforcement
location (EmailTemplate class + EmailTemplateService.DeleteTemplateAsync /
IEmailTemplateRepository.DeleteAsync) so behavior and documentation stay in
sync.

In
`@src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs`:
- Around line 64-69: A detecção de duplicidade por texto na exceção dentro de
ProviderVerificationStatusUpdatedIntegrationEventHandler é frágil; replace o
filtro "duplicate key" usando o utilitário PostgreSqlExceptionProcessor que
identifica violações pelo SQLSTATE 23505. Altere o bloco try/catch para capturar
Exception ex (sem filtrar por mensagem) e dentro do catch chamar
PostgreSqlExceptionProcessor.IsUniqueViolation(ex) (ou o método equivalente
existente) — se retornar true, executar o mesmo logger.LogInformation(...)
existente para pular o processamento; caso contrário, rethrow ou tratar a
exceção conforme o padrão do projeto. Certifique-se de usar a mesma assinatura
de logger e de importar/receber PostgreSqlExceptionProcessor conforme usado em
RegisterProviderCommandHandler e GlobalExceptionHandler.
- Around line 36-40: integrationEvent.NewStatus is coming from an enum (e.g.,
"Verified", "Rejected", "Pending") so the current check NewStatus.ToLower() ==
"approved" is incorrect and causes every non-"approved" value to be treated as
rejection; update the logic in the handler to normalize NewStatus using
ToLowerInvariant() (and Trim()) and explicitly map the expected enum string
values ("verified" -> use templateKey "provider-verification-approved",
"rejected" -> "provider-verification-rejected", "pending" -> e.g.
"provider-verification-pending" or a safe default) instead of a simple equality
to "approved", and use the same normalizedStatus value when building
correlationId (e.g.,
$"verification_status_update:{integrationEvent.ProviderId}:{normalizedStatus}")
so correlationId and template selection remain consistent.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Line 214: O bloco try/catch que envolve
context.Database.ExecuteSqlRawAsync("CREATE EXTENSION IF NOT EXISTS postgis;")
está com um catch vazio que oculta falhas; altere-o para capturar a exceção
(catch (Exception ex)) e registrar ou propagar o erro para diagnóstico — por
exemplo, usar o logger de teste ou Console/Error (ou rethrow como throw;) dentro
do catch para incluir ex.Message/stacktrace, garantindo que falhas na criação da
extensão PostGIS sejam visíveis.

---

Nitpick comments:
In `@src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs`:
- Line 9: Remove the System.Text.Json attribute from the domain enum: delete the
[JsonConverter(typeof(JsonStringEnumConverter))] annotation on ESubscriptionTier
in the Domain enum file so the Domain layer is not coupled to System.Text.Json;
keep serialization policy in the Contracts enum
(src/Contracts/.../ESubscriptionTier.cs) and rely on the existing explicit
mapping in SearchProvidersModuleApi (methods around SearchProvidersModuleApi.cs
lines ~250–269) to convert between the Contract and Domain enum types.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Around line 277-301: The retry loop in ApplyMigrationForContextAsync currently
retries on any Exception; change it so only transient errors trigger retries:
keep the existing catch (Npgsql.PostgresException ex) when (ex.SqlState ==
"57P01") for that transient case, add explicit transient checks (e.g., Postgres
transient SqlState codes like "57P01","53300","08006" or host/timeout-related
exceptions, and transient exception types such as TimeoutException or transient
DbUpdateException) and only await Task.Delay/continue the loop for those; for
all other exceptions (caught by catch (Exception ex)) immediately log via
logger?.LogError(ex, ...) and rethrow (or set lastException and break) so
non-transient errors do not cause retries, preserving the final throw of
InvalidOperationException with lastException if needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f9d21847-de3a-4a5e-ba13-62b9521bc5d3

📥 Commits

Reviewing files that changed from the base of the PR and between 0677892 and 750e728.

📒 Files selected for processing (7)
  • docs/roadmap-current.md
  • src/Contracts/Shared/ECommunicationPriority.cs
  • src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs
  • src/Modules/Communications/Domain/Entities/OutboxMessage.cs
  • src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Contracts/Shared/ECommunicationPriority.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
  • src/Modules/Communications/Domain/Entities/OutboxMessage.cs

Comment thread docs/roadmap-current.md Outdated
Comment thread docs/roadmap-current.md
Comment thread tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs (1)

18-19: Validação de argumentos deve estar no construtor, não em cada requisição.

next e environment são parâmetros do construtor e devem ser validados uma única vez na construção, não em cada invocação do middleware. Validar a cada requisição adiciona overhead desnecessário.

♻️ Mover validação para o construtor
 public sealed class SecurityHeadersMiddleware(
     RequestDelegate next, 
     IWebHostEnvironment environment,
     ILogger<SecurityHeadersMiddleware>? logger = null)
 {
+    private readonly RequestDelegate _next = next ?? throw new ArgumentNullException(nameof(next));
+    private readonly IWebHostEnvironment _environment = environment ?? throw new ArgumentNullException(nameof(environment));
+
     public async Task InvokeAsync(HttpContext context)
     {
-        ArgumentNullException.ThrowIfNull(next);
-        ArgumentNullException.ThrowIfNull(environment);
-
         logger?.LogTrace("Adding security headers to response.");
         // ... resto do código
-        await next(context);
+        await _next(context);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`
around lines 18 - 19, Move the null-checks out of the per-request path: remove
ArgumentNullException.ThrowIfNull(next) and
ArgumentNullException.ThrowIfNull(environment) from the middleware invocation
method (Invoke or InvokeAsync) and add them once in the
SecurityHeadersMiddleware constructor (validate constructor parameters next and
environment there). Keep the constructor parameter names next and environment
and ensure the Invoke/InvokeAsync method no longer performs those checks to
avoid per-request overhead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`:
- Around line 22-29: The SecurityHeadersMiddleware currently appends
X-Frame-Options, X-Content-Type-Options and Referrer-Policy which are also added
by EnvironmentSpecificExtensions.UseProductionMiddlewares, causing duplicate
headers and inconsistent Referrer-Policy values; update
SecurityHeadersMiddleware to check for existing headers before adding (e.g., use
TryAdd-like behavior or if (!context.Response.Headers.ContainsKey(...)) ), align
the Referrer-Policy value with the production middleware (or centralize the
chosen policy) and consider moving all security header setup into a single
helper/extension used by both SecurityHeadersMiddleware and
UseProductionMiddlewares to avoid future divergence.
- Around line 36-44: The hardcoded CSP in SecurityHeadersMiddleware (the call to
context.Response.Headers.Append that sets Content-Security-Policy with
"https://*.Betenbough.com") is incorrect and duplicates the dynamic
ContentSecurityPolicyMiddleware; remove the static CSP header from
SecurityHeadersMiddleware so the existing ContentSecurityPolicyMiddleware is the
single source of truth, or if you must keep it, externalize the connect-src
value into configuration and read it (rather than the hardcoded Betenbough
domain) before appending the header; update SecurityHeadersMiddleware to stop
emitting conflicting CSP headers and reference the dynamic config used by
ContentSecurityPolicyMiddleware.

---

Nitpick comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`:
- Around line 18-19: Move the null-checks out of the per-request path: remove
ArgumentNullException.ThrowIfNull(next) and
ArgumentNullException.ThrowIfNull(environment) from the middleware invocation
method (Invoke or InvokeAsync) and add them once in the
SecurityHeadersMiddleware constructor (validate constructor parameters next and
environment there). Keep the constructor parameter names next and environment
and ensure the Invoke/InvokeAsync method no longer performs those checks to
avoid per-request overhead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 47bbd978-dfa2-4995-b7af-7a0315dea89f

📥 Commits

Reviewing files that changed from the base of the PR and between 750e728 and d960612.

📒 Files selected for processing (3)
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs

Comment thread src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs Outdated
Comment thread src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs Outdated
…domain decoupling, robust idempotency and test resilience
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
docs/roadmap-current.md (3)

874-883: ⚠️ Potential issue | 🟡 Minor

Formatação Markdown: Adicionar linhas em branco ao redor da tabela.

A tabela de mapeamento de eventos (linhas 876-882) viola a regra MD058 do markdownlint, que exige uma linha em branco antes e depois de cada tabela.

🔧 Correção sugerida
 **Mapeamento de Integração com Eventos**:
+
 | Evento existente | Ação de Comunicação |
 |----------------|-------------------|
 | `UserRegisteredIntegrationEvent` | ✅ Enviar e-mail de boas-vindas |
 | `ProviderAwaitingVerificationIntegrationEvent` | ✅ Notificar administrador |
 | `ProviderVerificationStatusUpdatedIntegrationEvent` | ✅ Notificar prestador |
 | `DocumentVerifiedIntegrationEvent` | → Notificar prestador |
 | `DocumentRejectedIntegrationEvent` | → Notificar prestador |
+
 ---
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 874 - 883, The Markdown table under the
heading "**Mapeamento de Integração com Eventos**" currently lacks blank lines
before and after it (contains rows with events like
`UserRegisteredIntegrationEvent`,
`ProviderAwaitingVerificationIntegrationEvent`, etc.); update the document so
there is an empty line immediately before the table header line and an empty
line immediately after the final table row to satisfy MD058 (ensure the blank
line placement surrounds the entire table block).

976-977: ⚠️ Potential issue | 🟡 Minor

Bloco de código sem especificação de linguagem (MD040).

O bloco de código na linha 976 não possui um identificador de linguagem, violando a regra MD040 do markdownlint.

🔧 Correção sugerida
-   ```
+   ```text
    Event Occurred → Save to Outbox Table (Mesma Transação) → Background Worker processa Outbox
    ```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 976 - 977, The fenced code block
containing "Evento Ocorrido → Salvar na Tabela de Outbox (Mesma Transação) →
Trabalhador de Background processa Outbox" lacks a language identifier (MD040);
update that block by adding a language tag such as text (i.e., change ``` to
```text) so markdownlint recognizes it and the block renders correctly, locate
the fenced block by its content and modify the opening fence only.

659-659: ⚠️ Potential issue | 🟡 Minor

Inconsistência parcialmente resolvida: Exceção de escopo precisa ser mais explícita.

A linha 659 lista a implementação do Módulo de Comunicações como tarefa da Sprint 9, que é descrita como "BUFFER" sem novas funcionalidades. Embora a linha 668 declare explicitamente uma exceção para este módulo, seria mais claro reforçar essa exceção diretamente na lista de tarefas para evitar ambiguidade no planejamento.

📝 Sugestão de clarificação
-- [ ] **Módulo de Comunicações**: Implementar infraestrutura base (outbox pattern, modelos, handlers de evento)
+- [ ] **Módulo de Comunicações** *(Exceção planejada à regra de buffer)*: Implementar infraestrutura base (outbox pattern, modelos, handlers de evento)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` at line 659, Atualize a checklist do item "Módulo de
Comunicações" para incluir explicitamente a exceção inline (ex.: "exceção:
BUFFER — sem implementação nesta sprint") em vez de depender apenas da
declaração posterior; localize a linha contendo "**Módulo de Comunicações**:
Implementar infraestrutura base (outbox pattern, modelos, handlers de evento)" e
altere o texto para deixar claro que o trabalho está adiado/é uma exceção nesta
sprint, correspondendo à declaração de exceção já presente mais adiante no
documento.
🧹 Nitpick comments (1)
src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs (1)

51-54: Mensagem de exceção deve estar em português conforme política de linguagem.

A mensagem de exceção na linha 53 está em inglês. Se essa exceção propagar até a camada de API sem tratamento, o usuário final receberá uma mensagem em inglês. Conforme a política do repositório, mensagens voltadas ao usuário devem estar em português.

♻️ Proposta de tradução
 if (template.IsSystemTemplate)
 {
-    throw new InvalidOperationException($"Cannot delete system template with ID {id}.");
+    throw new InvalidOperationException($"Não é possível excluir o template de sistema com ID {id}.");
 }

Based on learnings: "User-facing validation messages and API responses shown to the frontend must be in Portuguese."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs`
around lines 51 - 54, The exception message thrown in the
EmailTemplateRepository (throw new InvalidOperationException($"Cannot delete
system template with ID {id}.")) is in English; replace it with a Portuguese
user-facing message (for example: $"Não é possível excluir o template do sistema
com ID {id}.") and ensure the change is applied to the InvalidOperationException
thrown when template.IsSystemTemplate is true; also scan for any other
user-facing exception messages in the same class (e.g., other
InvalidOperationException uses) and localize them to Portuguese as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/roadmap-current.md`:
- Line 870: Docs claim IsSystemTemplate protects templates from deletion but the
domain model only defines the property without enforcement; either update the
docs to mark this as a planned rule or implement the check. To implement, add a
validation in the deletion flow (e.g., EmailTemplateRepository.DeleteAsync or
EmailTemplateService.DeleteAsync) to fetch the EmailTemplate by id, return a
not-found result if null, and return a forbidden/failure result when
template.IsSystemTemplate is true before performing the delete; alternatively,
change the roadmap text around IsSystemTemplate to state it is planned and not
yet enforced.

In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs`:
- Around line 9-12: O método AddAsync está apenas chamando
context.EmailTemplates.AddAsync(template, cancellationToken) sem persistir
(SaveChangesAsync), enquanto DeleteAsync usa SaveChangesAsync; para tornar o
padrão consistente, faça AddAsync também chamar await
context.SaveChangesAsync(cancellationToken) após AddAsync (ou, alternativamente,
documente e aplique um padrão de UnitOfWork em todos os repositórios),
garantindo que o método AddAsync(EmailTemplate template, CancellationToken
cancellationToken) persista como DeleteAsync faz.

---

Duplicate comments:
In `@docs/roadmap-current.md`:
- Around line 874-883: The Markdown table under the heading "**Mapeamento de
Integração com Eventos**" currently lacks blank lines before and after it
(contains rows with events like `UserRegisteredIntegrationEvent`,
`ProviderAwaitingVerificationIntegrationEvent`, etc.); update the document so
there is an empty line immediately before the table header line and an empty
line immediately after the final table row to satisfy MD058 (ensure the blank
line placement surrounds the entire table block).
- Around line 976-977: The fenced code block containing "Evento Ocorrido →
Salvar na Tabela de Outbox (Mesma Transação) → Trabalhador de Background
processa Outbox" lacks a language identifier (MD040); update that block by
adding a language tag such as text (i.e., change ``` to ```text) so markdownlint
recognizes it and the block renders correctly, locate the fenced block by its
content and modify the opening fence only.
- Line 659: Atualize a checklist do item "Módulo de Comunicações" para incluir
explicitamente a exceção inline (ex.: "exceção: BUFFER — sem implementação nesta
sprint") em vez de depender apenas da declaração posterior; localize a linha
contendo "**Módulo de Comunicações**: Implementar infraestrutura base (outbox
pattern, modelos, handlers de evento)" e altere o texto para deixar claro que o
trabalho está adiado/é uma exceção nesta sprint, correspondendo à declaração de
exceção já presente mais adiante no documento.

---

Nitpick comments:
In
`@src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs`:
- Around line 51-54: The exception message thrown in the EmailTemplateRepository
(throw new InvalidOperationException($"Cannot delete system template with ID
{id}.")) is in English; replace it with a Portuguese user-facing message (for
example: $"Não é possível excluir o template do sistema com ID {id}.") and
ensure the change is applied to the InvalidOperationException thrown when
template.IsSystemTemplate is true; also scan for any other user-facing exception
messages in the same class (e.g., other InvalidOperationException uses) and
localize them to Portuguese as well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9875a98d-8ed6-44d2-be1b-1cb6717e7581

📥 Commits

Reviewing files that changed from the base of the PR and between d960612 and 742447a.

📒 Files selected for processing (8)
  • docs/roadmap-current.md
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs
  • src/Modules/Communications/Domain/Repositories/IEmailTemplateRepository.cs
  • src/Modules/Communications/Infrastructure/Persistence/Repositories/EmailTemplateRepository.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
✅ Files skipped from review due to trivial changes (2)
  • src/Modules/Communications/Application/Handlers/ProviderAwaitingVerificationIntegrationEventHandler.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Modules/Communications/Application/Handlers/ProviderActivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/ProviderVerificationStatusUpdatedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/UserRegisteredIntegrationEventHandler.cs

Comment thread docs/roadmap-current.md Outdated
Filipe Frigini added 2 commits April 9, 2026 11:27
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
docs/roadmap-current.md (1)

867-871: ⚠️ Potential issue | 🟠 Major

Valide o enforcement real de IsSystemTemplate antes de afirmar “Garantido”.

O texto afirma bloqueio de deleção em EmailTemplateRepository.DeleteAsync, mas este diff não mostra a regra. Se a validação não estiver implementada, isso vira promessa incorreta de comportamento.

#!/bin/bash
# Verificar se existe validação de IsSystemTemplate no fluxo de deleção de templates.

set -euo pipefail

echo "## Localizando repositório/serviço de template"
rg -n --type=cs -C3 'class\s+.*EmailTemplate.*Repository|interface\s+IEmailTemplateRepository|DeleteAsync\s*\('

echo
echo "## Procurando guard clause com IsSystemTemplate em deleção"
rg -n --type=cs -C4 'DeleteAsync\s*\(|IsSystemTemplate|Forbidden|ValidationException|DomainException'

echo
echo "## Procurando endpoint/serviço que chama deleção de template"
rg -n --type=cs -C3 'Delete.*Template|Template.*Delete|ICommunicationsModuleApi|EmailTemplate'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 867 - 871, Update
EmailTemplateRepository.DeleteAsync to actually enforce the IsSystemTemplate
flag: locate the DeleteAsync method in EmailTemplateRepository (and any
higher-level service methods that call it) and add a guard that checks the
EmailTemplate.IsSystemTemplate property, throwing a clear
domain/validation/forbidden exception (e.g.,
ValidationException/DomainException/ForbiddenException used elsewhere) when
true; also ensure any API/endpoint that delegates deletion respects and surfaces
that exception so the deletion cannot proceed and logs/returns an appropriate
error.
🧹 Nitpick comments (3)
docs/roadmap-current.md (1)

887-891: Evite redeclarar ECommunicationPriority no exemplo da interface.

A nota diz para usar tipos compartilhados, mas o snippet declara enum localmente. Prefira referenciar o enum de MeAjudaAi.Shared.Contracts para reduzir drift documental.

✏️ Ajuste sugerido
- public enum ECommunicationPriority { Low = 0, Normal = 1, High = 2 }
-
  public interface ICommunicationsModuleApi : IModuleApi
  {
      // E-mail
      Task<Result<Guid>> SendEmailAsync(
          EmailMessageDto email, 
-         ECommunicationPriority priority = ECommunicationPriority.Normal,
+         ECommunicationPriority priority = ECommunicationPriority.Normal,
          CancellationToken ct = default);
// Nota no texto (fora do bloco): 
// ECommunicationPriority vem de MeAjudaAi.Shared.Contracts.Enums

Based on learnings, prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` around lines 887 - 891, Remova a declaração local do
enum ECommunicationPriority do bloco de exemplo e substitua por uma nota textual
apontando que o enum é proveniente de MeAjudaAi.Shared.Contracts (ou
MeAjudaAi.Shared.Contracts.Enums); ou seja, elimine o snippet "public enum
ECommunicationPriority { ... }" e adicione uma linha comentada fora do bloco de
código indicando "ECommunicationPriority vem de
MeAjudaAi.Shared.Contracts.Enums" para garantir que o exemplo referencia o tipo
compartilhado em vez de redeclará‑lo.
src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs (1)

13-19: Dependência IWebHostEnvironment não utilizada.

O parâmetro environment é injetado e armazenado em _environment, mas nunca é utilizado no método InvokeAsync ou em qualquer outro lugar da classe. Isso parece ser código residual de uma implementação anterior.

♻️ Remover dependência não utilizada
 public sealed class SecurityHeadersMiddleware(
     RequestDelegate next, 
-    IWebHostEnvironment environment,
     ILogger<SecurityHeadersMiddleware>? logger = null)
 {
     private readonly RequestDelegate _next = next ?? throw new ArgumentNullException(nameof(next));
-    private readonly IWebHostEnvironment _environment = environment ?? throw new ArgumentNullException(nameof(environment));

E remover os usings não utilizados:

-using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`
around lines 13 - 19, O construtor da classe SecurityHeadersMiddleware injeta e
armazena IWebHostEnvironment em _environment, mas esse campo não é usado (nem em
InvokeAsync), então remova o parâmetro environment do construtor e a
inicialização/declaração do campo _environment, atualize a assinatura de
SecurityHeadersMiddleware para só receber RequestDelegate next e
ILogger<SecurityHeadersMiddleware>? logger = null, e remova quaisquer using
relacionados a IWebHostEnvironment/usings não utilizados; verifique também
chamadas/registro de middleware que instanciam SecurityHeadersMiddleware para
ajustar a nova assinatura.
tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersApiTests.cs (1)

130-130: Use enum de contrato compartilhado para valores de tier, não literals.

Linhas 130 e 149 usam abordagens inconsistentes ("Gold" vs 1). Prefira importar do Shared.Contracts e usar nameof() ou o enum diretamente para garantir segurança em refactors.

♻️ Proposta
+using MeAjudaAi.Contracts.Modules.SearchProviders.Enums;
@@
-        var subscriptionTier = "Gold";
+        var subscriptionTier = nameof(ESubscriptionTier.Gold);

Também na linha 149:

-        var subscriptionTier = 1; // Standard
+        var subscriptionTier = nameof(ESubscriptionTier.Standard);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersApiTests.cs`
at line 130, Replace the hard-coded tier literals with the shared contract enum:
in SearchProvidersApiTests replace the string assignment to subscriptionTier
("Gold") and the numeric literal (1) with the Shared.Contracts SubscriptionTier
enum (e.g., SubscriptionTier.Gold) or use nameof(SubscriptionTier.Gold) if a
string name is required; update the using/import to include Shared.Contracts and
change references to use SubscriptionTier.Gold to ensure refactor-safety and
consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`:
- Around line 46-50: O header Permissions-Policy definido em
SecurityHeadersMiddleware é código inativo porque
ContentSecurityPolicyMiddleware (registrado via UseApiServices) já adiciona
Permissions-Policy primeiro; remova as linhas que checam/appendam
Permissions-Policy em SecurityHeadersMiddleware (as linhas contendo
context.Response.Headers.ContainsKey(...) e
context.Response.Headers.Append(...)) ou, alternativamente, consolide o
comportamento movendo toda a lógica de gestão de Permissions-Policy para um
único middleware (preferencialmente ContentSecurityPolicyMiddleware) e elimine a
redundância em SecurityHeadersMiddleware para evitar valores conflitantes.

In `@src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs`:
- Line 1: Remova a dependência de serialização do enum de domínio
ESubscriptionTier: eliminar o using System.Text.Json.Serialization e qualquer
atributo [JsonConverter] do enum em
src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs; em seguida,
atualize a DTO SearchableProviderDto para importar e referenciar o enum do
contrato (using MeAjudaAi.Contracts.Modules.SearchProviders.Enums e usar esse
ESubscriptionTier contratual) em vez do enum de domínio; mantenha o
[JsonConverter] apenas no enum presente em
src/Contracts/.../ESubscriptionTier.cs para centralizar a configuração de
serialização na camada de contrato/API.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Around line 187-193: Ajuste a resolução de dependências para que
TestModule.Providers inclua implicitamente TestModule.ServiceCatalogs (além do
atual comportamento onde apenas SearchProviders expande dependências): ao
calcular módulos requisitados (onde atualmente você tem a lógica que adiciona
ServiceCatalogs/Locations quando modules.HasFlag(TestModule.SearchProviders)),
adicione uma condição para que se modules.HasFlag(TestModule.Providers) então
modules |= TestModule.ServiceCatalogs; isso garante que quando RequiredModules =
TestModule.Providers a migração de Providers (por exemplo na rotina que executa
migrations na linha referente a Providers) também eleve ServiceCatalogs antes de
prosseguir.
- Around line 295-303: No trate toda DbUpdateException como transitória: dentro
del bloque catch que asigna lastException y calcula isTransient (la comprobación
que actualmente incluye "ex is DbUpdateException"), elimine esa inclusión y
limite isTransient a TimeoutException y a Npgsql.PostgresException con los
SQLSTATEs transitórios ("57P01", "53300", "08006"); ajuste la expresión que
calcula isTransient (referencia: la variable isTransient en el catch junto a
lastException y attempt) para que solo considere TimeoutException y las
PostgresException con esos SqlState, de modo que errores determinísticos de
migración no se reintenten.

---

Duplicate comments:
In `@docs/roadmap-current.md`:
- Around line 867-871: Update EmailTemplateRepository.DeleteAsync to actually
enforce the IsSystemTemplate flag: locate the DeleteAsync method in
EmailTemplateRepository (and any higher-level service methods that call it) and
add a guard that checks the EmailTemplate.IsSystemTemplate property, throwing a
clear domain/validation/forbidden exception (e.g.,
ValidationException/DomainException/ForbiddenException used elsewhere) when
true; also ensure any API/endpoint that delegates deletion respects and surfaces
that exception so the deletion cannot proceed and logs/returns an appropriate
error.

---

Nitpick comments:
In `@docs/roadmap-current.md`:
- Around line 887-891: Remova a declaração local do enum ECommunicationPriority
do bloco de exemplo e substitua por uma nota textual apontando que o enum é
proveniente de MeAjudaAi.Shared.Contracts (ou MeAjudaAi.Shared.Contracts.Enums);
ou seja, elimine o snippet "public enum ECommunicationPriority { ... }" e
adicione uma linha comentada fora do bloco de código indicando
"ECommunicationPriority vem de MeAjudaAi.Shared.Contracts.Enums" para garantir
que o exemplo referencia o tipo compartilhado em vez de redeclará‑lo.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`:
- Around line 13-19: O construtor da classe SecurityHeadersMiddleware injeta e
armazena IWebHostEnvironment em _environment, mas esse campo não é usado (nem em
InvokeAsync), então remova o parâmetro environment do construtor e a
inicialização/declaração do campo _environment, atualize a assinatura de
SecurityHeadersMiddleware para só receber RequestDelegate next e
ILogger<SecurityHeadersMiddleware>? logger = null, e remova quaisquer using
relacionados a IWebHostEnvironment/usings não utilizados; verifique também
chamadas/registro de middleware que instanciam SecurityHeadersMiddleware para
ajustar a nova assinatura.

In
`@tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersApiTests.cs`:
- Line 130: Replace the hard-coded tier literals with the shared contract enum:
in SearchProvidersApiTests replace the string assignment to subscriptionTier
("Gold") and the numeric literal (1) with the Shared.Contracts SubscriptionTier
enum (e.g., SubscriptionTier.Gold) or use nameof(SubscriptionTier.Gold) if a
string name is required; update the using/import to include Shared.Contracts and
change references to use SubscriptionTier.Gold to ensure refactor-safety and
consistency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3dc9dbd1-2cf2-4ac3-93af-6414184cd587

📥 Commits

Reviewing files that changed from the base of the PR and between 742447a and a578cab.

📒 Files selected for processing (8)
  • docs/roadmap-current.md
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs
  • src/Contracts/Contracts/Modules/SearchProviders/Enums/ESubscriptionTier.cs
  • src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersApiTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs

Comment thread src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs Outdated
Comment thread src/Modules/SearchProviders/Domain/Enums/ESubscriptionTier.cs Outdated
Comment thread tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs Outdated
Comment thread tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs Outdated
…epository, subscription tier enum, and CI/CD pipeline configuration
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/roadmap-current.md (2)

8-8: ⚠️ Potential issue | 🟡 Minor

Atualize o status global no topo para refletir o encerramento da Sprint 9.

A linha ainda diz “Sprint 9 (Estabilização) em andamento (26 Mar 2026)”, mas a própria seção da Sprint 9 já está como encerrada em 11 Abr 2026. Isso gera leitura conflitante no cabeçalho do documento.

✏️ Sugestão objetiva
-> **📅 Status Atual**: Sprint 9 (Estabilização) em andamento (26 Mar 2026)  
+> **📅 Status Atual**: Sprint 9 (Estabilização) encerrada (11 Abr 2026)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` at line 8, Atualize o cabeçalho que atualmente
contém "**📅 Status Atual**: Sprint 9 (Estabilização) em andamento (26 Mar
2026)" para refletir que a Sprint 9 foi encerrada em 11 Abr 2026; localize e
substitua esse texto no topo do documento (docs/roadmap-current.md) por uma
versão como "**📅 Status Atual**: Sprint 9 (Estabilização) encerrada (11 Abr
2026)" ou outra formulação consistente com a seção "Sprint 9" para evitar
conflito de leitura.

454-454: ⚠️ Potential issue | 🟡 Minor

Há inconsistência de status da Sprint 5.5 no próprio documento.

Em um ponto está “✅ CONCLUÍDO”, e na seção detalhada está “🔄 EM ANDAMENTO - Aguardando CI/CD”. Defina explicitamente se é snapshot histórico ou status atual para não confundir leitura executiva.

Also applies to: 512-513

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` at line 454, Há uma inconsistência entre a linha que
mostra "Status: ✅ CONCLUÍDO (Sprint 5.5 - 19 Dez 2025)" e a seção detalhada que
ainda mostra "🔄 EM ANDAMENTO - Aguardando CI/CD"; escolha e aplique uma única
fonte de verdade: ou mantenha como snapshot histórico (ex.: "Snapshot —
CONCLUÍDO em 19 Dez 2025") ou como status atual, e atualize ambas ocorrências
correspondentes (a linha "Status: ✅ CONCLUÍDO (Sprint 5.5 - 19 Dez 2025)" e a
seção detalhada que contém "🔄 EM ANDAMENTO - Aguardando CI/CD") para usar a
mesma texto/ícone; se optar por snapshot, inclua uma nota de data/contexto
visível; se optar por status atual, remova/altere a marcação de snapshot para
evitar ambiguidade.
♻️ Duplicate comments (2)
docs/roadmap-current.md (1)

867-867: ⚠️ Potential issue | 🟠 Major

Evite afirmar “garantido” sem evidência explícita de enforcement no texto técnico.

Aqui o roadmap afirma que a proteção de deleção por IsSystemTemplate é garantida em EmailTemplateRepository.DeleteAsync; se essa validação não estiver claramente implementada, o documento pode prometer comportamento inexistente.

✏️ Ajuste seguro de documentação
-- **IsSystemTemplate**: Flag para proteger modelos críticos de deleção (Garantido em `EmailTemplateRepository.DeleteAsync`).
+- **IsSystemTemplate**: Flag para proteger modelos críticos de deleção (regra aplicada na camada de aplicação/repositório; manter este trecho alinhado com a implementação vigente).

Based on learnings, foi priorizada a consistência entre contratos compartilhados/implementação para evitar drift documental.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/roadmap-current.md` at line 867, The doc line claims deletion protection
is "guaranteed" by IsSystemTemplate in EmailTemplateRepository.DeleteAsync;
change the wording to avoid asserting enforcement unless the repository method
explicitly implements the check—either remove "Garantido" and state "intended to
prevent deletion when IsSystemTemplate is true" or update the doc to "enforced
in EmailTemplateRepository.DeleteAsync" only after verifying that DeleteAsync
contains a concrete IsSystemTemplate validation and throws/returns
appropriately; if enforcement exists, add a brief note referencing
EmailTemplateRepository.DeleteAsync as the enforcement point, otherwise note it
as a design intention and file a follow-up task to implement the check.
src/Modules/Communications/Application/Services/OutboxProcessorService.cs (1)

74-75: ⚠️ Potential issue | 🟡 Minor

Padronize estes comentários para português.

Os comentários inline das Lines 74 e 97 ainda estão em inglês. Mantém o arquivo consistente com o padrão do repositório.

Based on learnings, comments and XML documentation in C# should be written in Portuguese across the codebase.

Also applies to: 97-97

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`
around lines 74 - 75, Padronize os comentários em inglês para português no
arquivo, substituindo os comentários inline atuais nas proximidades de
OutboxProcessorService (comentário acima de SaveChanges na classe
OutboxProcessorService) e o comentário na linha referida como 97 por versões em
português coerentes com o padrão do repositório; atualize a redação mantendo o
mesmo significado técnico e estilo de comentário usado no restante do projeto
(por exemplo, traduzir "Remove immediate SaveChanges to allow
OutboxProcessorBase to commit everything together" para uma versão em português
clara e equivalente).
🧹 Nitpick comments (1)
src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs (1)

597-603: Evite registrar SecurityHeadersMiddleware duas vezes.

UseSecurityHardening() adiciona o mesmo middleware que já entra via app.UseApiMiddlewares() em src/Bootstrapper/MeAjudaAi.ApiService/Extensions/ServiceCollectionExtensions.cs:164. Hoje isso tende a ser idempotente, mas ainda duplica callbacks OnStarting e deixa a ordem de aplicação dos headers mais difícil de raciocinar. Centralize esse middleware em um único ponto do pipeline.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs`
around lines 597 - 603, The UseSecurityHardening method is registering
SecurityHeadersMiddleware redundantly; remove the
app.UseMiddleware<SecurityHeadersMiddleware>() call from UseSecurityHardening
and rely on the single registration already performed by UseApiMiddlewares (the
method that currently wires SecurityHeadersMiddleware into the pipeline), so the
middleware is only added once and header ordering/OnStarting callbacks remain
deterministic; keep UseSecurityHardening for other hardening steps if present
but do not re-register SecurityHeadersMiddleware.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/roadmap-current.md`:
- Line 1460: O sumário executivo mostra "⏳ **Sprint 9: BUFFER E MITIGAÇÃO DE
RISCO**" enquanto o corpo registra "✅ ENCERRADA (11 Abr 2026)"; alinhe-os
atualizando o bloco do sumário para refletir o encerramento — substitua o ícone
e texto na entrada do sumário (a string contendo "⏳ **Sprint 9: BUFFER E
MITIGAÇÃO DE RISCO**") para usar "✅" e indicar "ENCERRADA (11 Abr 2026)" igual
ao corpo.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/ServiceCollectionExtensions.cs`:
- Around line 172-174: A chamada a app.UseAntiforgery() está antes da
autenticação/autorization; atualize a ordem no método de configuração em
ServiceCollectionExtensions.cs movendo app.UseAntiforgery() para depois de
app.UseAuthentication() e, especificamente, após app.UseAuthorization() (que já
existe no arquivo) para garantir que antiforgery execute somente após o usuário
ser autenticado/autoriza-do.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Around line 65-75: The CommunicationLog creation/adding
(CommunicationLog.CreateSuccess and logRepository.AddAsync) must not be allowed
to throw and break the OutboxProcessorBase commit flow called in
OutboxProcessorBase.ProcessPendingMessagesAsync (which waits until
OnSuccessAsync/OnFailureAsync finish), so wrap the log creation + await
logRepository.AddAsync(...) in a try/catch that catches and records the logging
failure (e.g., processLogger.Error with correlationId/message.Id and the
exception) but does not rethrow; apply the same pattern to the failure-path code
that calls CommunicationLog.CreateFailure and logRepository.AddAsync (and ensure
MarkAsFailed is still executed/persisted by not letting log errors bubble up).

---

Outside diff comments:
In `@docs/roadmap-current.md`:
- Line 8: Atualize o cabeçalho que atualmente contém "**📅 Status Atual**:
Sprint 9 (Estabilização) em andamento (26 Mar 2026)" para refletir que a Sprint
9 foi encerrada em 11 Abr 2026; localize e substitua esse texto no topo do
documento (docs/roadmap-current.md) por uma versão como "**📅 Status Atual**:
Sprint 9 (Estabilização) encerrada (11 Abr 2026)" ou outra formulação
consistente com a seção "Sprint 9" para evitar conflito de leitura.
- Line 454: Há uma inconsistência entre a linha que mostra "Status: ✅ CONCLUÍDO
(Sprint 5.5 - 19 Dez 2025)" e a seção detalhada que ainda mostra "🔄 EM
ANDAMENTO - Aguardando CI/CD"; escolha e aplique uma única fonte de verdade: ou
mantenha como snapshot histórico (ex.: "Snapshot — CONCLUÍDO em 19 Dez 2025") ou
como status atual, e atualize ambas ocorrências correspondentes (a linha
"Status: ✅ CONCLUÍDO (Sprint 5.5 - 19 Dez 2025)" e a seção detalhada que contém
"🔄 EM ANDAMENTO - Aguardando CI/CD") para usar a mesma texto/ícone; se optar
por snapshot, inclua uma nota de data/contexto visível; se optar por status
atual, remova/altere a marcação de snapshot para evitar ambiguidade.

---

Duplicate comments:
In `@docs/roadmap-current.md`:
- Line 867: The doc line claims deletion protection is "guaranteed" by
IsSystemTemplate in EmailTemplateRepository.DeleteAsync; change the wording to
avoid asserting enforcement unless the repository method explicitly implements
the check—either remove "Garantido" and state "intended to prevent deletion when
IsSystemTemplate is true" or update the doc to "enforced in
EmailTemplateRepository.DeleteAsync" only after verifying that DeleteAsync
contains a concrete IsSystemTemplate validation and throws/returns
appropriately; if enforcement exists, add a brief note referencing
EmailTemplateRepository.DeleteAsync as the enforcement point, otherwise note it
as a design intention and file a follow-up task to implement the check.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Around line 74-75: Padronize os comentários em inglês para português no
arquivo, substituindo os comentários inline atuais nas proximidades de
OutboxProcessorService (comentário acima de SaveChanges na classe
OutboxProcessorService) e o comentário na linha referida como 97 por versões em
português coerentes com o padrão do repositório; atualize a redação mantendo o
mesmo significado técnico e estilo de comentário usado no restante do projeto
(por exemplo, traduzir "Remove immediate SaveChanges to allow
OutboxProcessorBase to commit everything together" para uma versão em português
clara e equivalente).

---

Nitpick comments:
In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs`:
- Around line 597-603: The UseSecurityHardening method is registering
SecurityHeadersMiddleware redundantly; remove the
app.UseMiddleware<SecurityHeadersMiddleware>() call from UseSecurityHardening
and rely on the single registration already performed by UseApiMiddlewares (the
method that currently wires SecurityHeadersMiddleware into the pipeline), so the
middleware is only added once and header ordering/OnStarting callbacks remain
deterministic; keep UseSecurityHardening for other hardening steps if present
but do not re-register SecurityHeadersMiddleware.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 25dd146a-ea3a-4f7f-ae44-bd4e3eb5738c

📥 Commits

Reviewing files that changed from the base of the PR and between 75f2bf3 and 2c81974.

📒 Files selected for processing (16)
  • docs/roadmap-current.md
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/ServiceCollectionExtensions.cs
  • src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Infrastructure/Extensions.cs
  • src/Modules/Communications/Tests/Architecture/CommunicationsArchitectureTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentVerifiedIntegrationEventHandlerTests.cs
  • src/Modules/SearchProviders/Infrastructure/Events/Handlers/ServiceDeactivatedIntegrationEventHandler.cs
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceActivatedDomainEventHandler.cs
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceDeactivatedDomainEventHandler.cs
  • src/Shared/Database/Outbox/OutboxProcessorBase.cs
  • tests/MeAjudaAi.Integration.Tests/Middleware/SecurityHeadersMiddlewareTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
✅ Files skipped from review due to trivial changes (1)
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceDeactivatedDomainEventHandler.cs
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceActivatedDomainEventHandler.cs
  • src/Modules/SearchProviders/Infrastructure/Events/Handlers/ServiceDeactivatedIntegrationEventHandler.cs
  • src/Modules/Communications/Tests/Architecture/CommunicationsArchitectureTests.cs
  • src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs
  • tests/MeAjudaAi.Integration.Tests/Middleware/SecurityHeadersMiddlewareTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentVerifiedIntegrationEventHandlerTests.cs

Comment thread docs/roadmap-current.md
Comment thread src/Modules/Communications/Application/Services/OutboxProcessorService.cs Outdated
Filipe Frigini added 3 commits April 12, 2026 17:55
- Align Sprint 9 and 5.5 status in roadmap.
- Move UseAntiforgery after UseAuthorization in middleware pipeline.
- Remove redundant SecurityHeadersMiddleware registration.
- Improve logging resilience in OutboxProcessorService and translate comments.
- Refactor idempotency test to use typed UniqueConstraintException for better reliability.
…cessorBase to fix unit tests

- Fix ProcessPendingMessagesAsync_WhenSuccessfulDispatch_ShouldMarkAsSent test failure.
- Ensure message is explicitly marked as processing and persisted before dispatch.
- Implement AntiforgeryCookieMiddleware to ensure CSRF tokens are issued on GET requests (Hardening).
- Configure https_port in WebApplicationFactory to enable HttpsRedirection testing.
- Ensure deterministic middleware ordering for security headers and antiforgery.
@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 12, 2026

@corerabbitai review

Filipe Frigini added 2 commits April 12, 2026 18:34
- Unified UseAuthorization() and UseAntiforgery() in the correct pipeline order.
- Enhanced AntiforgeryCookieMiddleware with try-catch to prevent 500 errors in test environments.
- Confirmed all 161 E2E tests are passing.
…p status

- Updated BusinessMetrics to accept custom meter names for test isolation.
- Refactored BusinessMetricsTests to use unique meter names per instance.
- Finalized Sprint 9 status in roadmap header.
- Guaranteed all tests (Unit, Integration, E2E) are passing after security and outbox changes.
@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 12, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (1)
tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs (1)

215-215: ⚠️ Potential issue | 🟠 Major

Não mascare PendingModelChangesWarning em todos os DbContexts.

Esse Ignore(...) global tira do harness o fail-fast para drift real entre modelo e migrations. Se existe falso positivo, limite a supressão ao contexto que realmente precisa dela ou a uma condição explícita de debug.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs` at line 215, Remova a
supressão global do aviso PendingModelChangesWarning aplicada via
options.ConfigureWarnings(w =>
w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
e, em vez disso, aplique-a apenas no DbContext específico que precisa (por ex.
no OnConfiguring/DbContextOptionsBuilder do YourSpecialDbContext) ou envolva-a
numa condição explícita de debug/flag de teste; localize a chamada
ConfigureWarnings e mova/condicione essa Ignore para o construtor/opções do
contexto alvo (ou remova-a completamente) para restaurar o comportamento
fail-fast nos demais DbContext.
🧹 Nitpick comments (3)
src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs (1)

34-82: Amplie os testes para cobrir ramos críticos do handler

Os testes atuais não cobrem falha de provider (IsSuccess=false), provider sem email e deduplicação por unique constraint. Vale adicionar esses cenários para blindar comportamento de erro e idempotência.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs`
around lines 34 - 82, Add three new unit tests for
DocumentRejectedIntegrationEventHandler: (1)
HandleAsync_WhenProviderFetchFails_ShouldSkipAndNotEnqueue — mock
_providersModuleApiMock.GetProviderByIdAsync to return Result.Failure(...) and
assert _outboxRepositoryMock.AddAsync/SaveChangesAsync are never called; (2)
HandleAsync_WhenProviderHasNoEmail_ShouldSkipEnqueue — return a
ModuleProviderDto with null/empty Email and assert no outbox operations; (3)
HandleAsync_WhenOutboxDuplicateExists_ShouldNotCreateDuplicate — simulate
deduplication by having the outbox repository indicate an existing OutboxMessage
(e.g., mock a query or make AddAsync throw a unique constraint exception) and
assert handler does not create a second message (verify AddAsync not called or
SaveChangesAsync not called, depending on how duplicates are detected). Use the
existing _handler.HandleAsync(integrationEvent), _providersModuleApiMock, and
_outboxRepositoryMock symbols to set up expectations and verifications.
src/Shared/Monitoring/BusinessMetrics.cs (1)

24-26: Extraia o nome padrão do meter para uma constante compartilhada.

O default ficou correto e mantém o DI estável, mas o literal "MeAjudaAi.Business" agora está duplicado aqui e também em testes que dependem do nome canônico. Centralizar isso no próprio BusinessMetrics reduz drift silencioso entre produção e suíte.

♻️ Refactor sugerido
 public class BusinessMetrics : IDisposable
 {
+    public const string DefaultMeterName = "MeAjudaAi.Business";
+
     private readonly Meter _meter;
     private readonly Counter<long> _userRegistrations;
     private readonly Counter<long> _userLogins;
@@
-    public BusinessMetrics(string meterName = "MeAjudaAi.Business")
+    public BusinessMetrics(string meterName = DefaultMeterName)
     {
         _meter = new Meter(meterName, "1.0.0");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Monitoring/BusinessMetrics.cs` around lines 24 - 26, Extraia o
literal do nome do meter para uma constante pública dentro da classe
BusinessMetrics e use essa constante como valor padrão do parâmetro do
construtor; por exemplo, adicionar um symbol BusinessMetrics.DefaultMeterName =
"MeAjudaAi.Business" e alterar o construtor BusinessMetrics(string meterName =
DefaultMeterName) que cria _meter = new Meter(meterName, "1.0.0"); e atualize os
testes para referenciar BusinessMetrics.DefaultMeterName em vez do string
literal para evitar duplicação e drift.
src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs (1)

23-23: Considere substituir payload anônimo por contrato tipado compartilhado.

Aqui, TemplateKey + campos do payload estão “soltos” no handler. Um DTO/contrato compartilhado reduz drift entre módulos e facilita evolução segura do schema da mensagem.

Based on learnings: prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.

Also applies to: 45-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs`
at line 23, O handler DocumentVerifiedIntegrationEventHandler está usando
TemplateKey = "document-verified" e um payload anônimo — substitua esse uso por
um compartilhado contrato tipado em Shared.Contracts (por exemplo
DocumentVerifiedEventDto ou ISharedDocumentVerifiedContract) e mova
constantes/enums reutilizáveis para Shared.Contracts; atualize o handler para
consumir esse DTO (em vez de campos soltos nas linhas 45-52), referenciar a
constante TemplateKey a partir do contrato/enum compartilhado e ajustar qualquer
serialização/deserialização para usar o novo tipo.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs`:
- Around line 565-567: The GlobalLimiter's permit calculation uses the wrong
config keys (it reads
RateLimit:AuthenticatedRequestsPerMinute/RateLimit:DefaultRequestsPerMinute)
while ValidateSecurityConfiguration() validates AdvancedRateLimit; update the
permit calculation in SecurityExtensions (the var permitLimit assignment inside
GlobalLimiter) to read AdvancedRateLimit:AuthenticatedRequestsPerMinute and
AdvancedRateLimit:AnonymousRequestsPerMinute (or otherwise align keys with
ValidateSecurityConfiguration()), so the limiter uses the same configured values
as the validation routine.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/ServiceCollectionExtensions.cs`:
- Around line 174-178: Add the antiforgery response header to the CORS exposed
headers so cross-origin clients can read the token: update the CORS
DefaultPolicy configuration (the builder where DefaultPolicy is created in
ServiceCollectionExtensions) to call WithExposedHeaders("X-XSRF-TOKEN"); this
ensures the header written by AntiforgeryCookieMiddleware ("X-XSRF-TOKEN") is
available to browser clients across origins while leaving other CORS settings
intact.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/AntiforgeryCookieMiddleware.cs`:
- Around line 20-34: The empty catch in AntiforgeryCookieMiddleware (around
_antiforgery.GetAndStoreTokens(context)) hides DataProtection/antiforgery
configuration errors; change the catch to capture the Exception (e.g., catch
(Exception ex)) and log it in English via an injected
ILogger<AntiforgeryCookieMiddleware> (include context info and ex) and only
swallow the exception for explicit test/bypass environments (use an injected
IHostEnvironment or environment flag such as
_env.IsEnvironment("Test")/_env.IsDevelopment()); in non-test/production
environments log at warning/error level and do not silently drop the error
(optionally rethrow or let middleware pipeline handle it) so observability is
preserved.

In
`@src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs`:
- Around line 74-80: The catch currently treats any
MeAjudaAi.Shared.Database.Exceptions.UniqueConstraintException as "already
enqueued" which can hide real integrity errors; in
DocumentRejectedIntegrationEventHandler adjust the catch handling for
processedException so you only swallow the exception when it specifically
matches the outbox/correlation unique constraint (e.g., check a ConstraintName
property, error code or message on processedException for the outbox correlation
constraint), and for other UniqueConstraintException instances rethrow or let
them bubble (or log as an error) instead; update the conditional around
processedException to inspect that identifier before returning.

In
`@src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs`:
- Around line 74-76: Centralize the outbox constraint name: define a shared
constant (e.g., OutboxMessageConstraints.CorrelationIdIndexName) and use it both
in the EF configuration class (add .HasName("...") in
OutboxMessageConfiguration) and in the DocumentVerifiedIntegrationEventHandler
when checking UniqueConstraintException.ConstraintName instead of the hardcoded
"ix_outbox_messages_correlation_id"; update the OutboxMessageConfiguration to
call HasName using that constant and change the handler's comparison to
reference the same constant to guarantee a single source of truth for the
constraint name.

In `@src/Modules/Communications/Application/Services/OutboxProcessorService.cs`:
- Around line 80-85: The log messages are in Portuguese; change them to English
to standardize runtime logs: update the logger.LogError call that logs "Falha ao
criar log de sucesso..." to an English message including the same contextual
placeholders (message.Id, message.CorrelationId), and update the
logger.LogInformation call that logs "Mensagem outbox ..." (and the other
logging block later in the file) to English messages preserving the placeholders
(message.Id, message.Channel, recipientMasked); ensure formatting and exception
parameterization remain unchanged.

In
`@src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceActivatedDomainEventHandler.cs`:
- Around line 21-25: The handler currently swallows missing-service cases by
logging and returning in the ServiceActivatedDomainEventHandler (check the
service == null branch and the logger.LogWarning call), which prevents retries;
replace the early return with throwing an exception (e.g.,
InvalidOperationException or a domain-specific exception) that includes
domainEvent.ServiceId and a clear message so the failure is propagated and the
event can be retried/handled by the retry/fault system.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Around line 248-255: Reordene as chamadas de ApplyMigrationForContextAsync em
BaseApiTest.cs para espelhar a prioridade do bootstrap de produção: chame nos
módulos na ordem Users -> ServiceCatalogs -> Locations -> Documents -> Providers
-> Communications (e adicione SearchProviders se existir no enum
TestModule/infra), usando os mesmos TestModule flags (TestModule.Users,
TestModule.ServiceCatalogs, TestModule.Locations, TestModule.Documents,
TestModule.Providers, TestModule.Communications, TestModule.SearchProviders)
para decidir quais migrations aplicar; ajuste a sequência de invocações que
atualmente usa
ApplyMigrationForContextAsync(serviceProvider.GetRequiredService<...DbContext>(),
"...", logger) para seguir exatamente essa ordem.

---

Duplicate comments:
In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Line 215: Remova a supressão global do aviso PendingModelChangesWarning
aplicada via options.ConfigureWarnings(w =>
w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning))
e, em vez disso, aplique-a apenas no DbContext específico que precisa (por ex.
no OnConfiguring/DbContextOptionsBuilder do YourSpecialDbContext) ou envolva-a
numa condição explícita de debug/flag de teste; localize a chamada
ConfigureWarnings e mova/condicione essa Ignore para o construtor/opções do
contexto alvo (ou remova-a completamente) para restaurar o comportamento
fail-fast nos demais DbContext.

---

Nitpick comments:
In
`@src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs`:
- Line 23: O handler DocumentVerifiedIntegrationEventHandler está usando
TemplateKey = "document-verified" e um payload anônimo — substitua esse uso por
um compartilhado contrato tipado em Shared.Contracts (por exemplo
DocumentVerifiedEventDto ou ISharedDocumentVerifiedContract) e mova
constantes/enums reutilizáveis para Shared.Contracts; atualize o handler para
consumir esse DTO (em vez de campos soltos nas linhas 45-52), referenciar a
constante TemplateKey a partir do contrato/enum compartilhado e ajustar qualquer
serialização/deserialização para usar o novo tipo.

In
`@src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs`:
- Around line 34-82: Add three new unit tests for
DocumentRejectedIntegrationEventHandler: (1)
HandleAsync_WhenProviderFetchFails_ShouldSkipAndNotEnqueue — mock
_providersModuleApiMock.GetProviderByIdAsync to return Result.Failure(...) and
assert _outboxRepositoryMock.AddAsync/SaveChangesAsync are never called; (2)
HandleAsync_WhenProviderHasNoEmail_ShouldSkipEnqueue — return a
ModuleProviderDto with null/empty Email and assert no outbox operations; (3)
HandleAsync_WhenOutboxDuplicateExists_ShouldNotCreateDuplicate — simulate
deduplication by having the outbox repository indicate an existing OutboxMessage
(e.g., mock a query or make AddAsync throw a unique constraint exception) and
assert handler does not create a second message (verify AddAsync not called or
SaveChangesAsync not called, depending on how duplicates are detected). Use the
existing _handler.HandleAsync(integrationEvent), _providersModuleApiMock, and
_outboxRepositoryMock symbols to set up expectations and verifications.

In `@src/Shared/Monitoring/BusinessMetrics.cs`:
- Around line 24-26: Extraia o literal do nome do meter para uma constante
pública dentro da classe BusinessMetrics e use essa constante como valor padrão
do parâmetro do construtor; por exemplo, adicionar um symbol
BusinessMetrics.DefaultMeterName = "MeAjudaAi.Business" e alterar o construtor
BusinessMetrics(string meterName = DefaultMeterName) que cria _meter = new
Meter(meterName, "1.0.0"); e atualize os testes para referenciar
BusinessMetrics.DefaultMeterName em vez do string literal para evitar duplicação
e drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5af23134-d466-4874-a686-e2747010c298

📥 Commits

Reviewing files that changed from the base of the PR and between 75f2bf3 and 0cf039f.

📒 Files selected for processing (21)
  • docs/roadmap-current.md
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/ServiceCollectionExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/AntiforgeryCookieMiddleware.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/appsettings.json
  • src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Infrastructure/Extensions.cs
  • src/Modules/Communications/Tests/Architecture/CommunicationsArchitectureTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentVerifiedIntegrationEventHandlerTests.cs
  • src/Modules/SearchProviders/Infrastructure/Events/Handlers/ServiceDeactivatedIntegrationEventHandler.cs
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceActivatedDomainEventHandler.cs
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceDeactivatedDomainEventHandler.cs
  • src/Shared/Database/Outbox/OutboxProcessorBase.cs
  • src/Shared/Monitoring/BusinessMetrics.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Middleware/SecurityHeadersMiddlewareTests.cs
  • tests/MeAjudaAi.Integration.Tests/Modules/SearchProviders/SearchProvidersE2ETests.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Monitoring/BusinessMetricsTests.cs
✅ Files skipped from review due to trivial changes (3)
  • src/Bootstrapper/MeAjudaAi.ApiService/appsettings.json
  • src/Modules/Communications/Tests/Architecture/CommunicationsArchitectureTests.cs
  • src/Shared/Database/Outbox/OutboxProcessorBase.cs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceDeactivatedDomainEventHandler.cs
  • tests/MeAjudaAi.Integration.Tests/Middleware/SecurityHeadersMiddlewareTests.cs
  • src/Modules/Communications/Infrastructure/Extensions.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentVerifiedIntegrationEventHandlerTests.cs

Comment thread src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs Outdated
Comment thread src/Modules/Communications/Application/Services/OutboxProcessorService.cs Outdated
Comment thread tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs Outdated
Filipe Frigini added 4 commits April 12, 2026 21:41
…view

- Refactored CompressionSecurityMiddleware to be more robust and avoid 'response has already started' error.
- Simplified integration tests by using /health endpoint to verify security policy without redirect side-effects.
- Configured BaseAddress to HTTPS in WebApplicationFactory to prevent header loss during redirects.
- Resolved ambiguous OutboxMessage references by using type aliases.
- Fully addressed all remaining items from the latest code review.
- Injected ILogger mock into middleware constructor.
- Updated assertions to match 'identity' encoding instead of removal.
- Added localized PendingModelChangesWarning suppression to BaseDatabaseTest and UserRepositoryIntegrationTests to handle naming convention drift during tests.
- Finalized translation of remaining comments and assertion messages in integration and business metrics files.
- Verified system stability with 161 E2E tests passing.
… failures

- Fixed AntiforgeryCookieMiddleware to always call _next(context) even when token generation fails in Test environment.
- Added /api/logout to sensitive paths in CompressionSecurityMiddleware.
- Verified all 207 Unit, 342 Integration, and 161 E2E tests are passing.
@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs (1)

44-44: Inconsistência no formato do correlationId entre handlers.

Este handler usa $"{templateKey}:..." enquanto DocumentVerifiedIntegrationEventHandler usa $"document_verified:..." hardcoded. Ambas abordagens funcionam para idempotência, mas padronizar facilitaria manutenção futura.

💡 Sugestão de padronização

Opção 1: Usar template key em ambos (como este handler):

var correlationId = $"{templateKey}:{integrationEvent.DocumentId}:{integrationEvent.ProviderId}";

Opção 2: Usar prefixo descritivo em ambos (como DocumentVerifiedIntegrationEventHandler):

var correlationId = $"document_rejected:{integrationEvent.DocumentId}:{integrationEvent.ProviderId}";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs`
at line 44, Padronize o formato de correlationId entre
DocumentRejectedIntegrationEventHandler e
DocumentVerifiedIntegrationEventHandler; escolha uma das abordagens sugeridas e
atualize a criação do correlationId dentro do método que constrói o evento (em
DocumentRejectedIntegrationEventHandler) para usar o mesmo padrão que o outro
handler (por exemplo, substituir var correlationId = $"{templateKey}:..."; por
um prefixo descritivo
$"document_rejected:{integrationEvent.DocumentId}:{integrationEvent.ProviderId}"
ou, alternativamente, alterar DocumentVerifiedIntegrationEventHandler para usar
templateKey em vez do literal), garantindo que ambos handlers usem o mesmo
formato para idempotência e atualize quaisquer testes ou consumidores que
dependam do formato antigo.
src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs (2)

49-77: Considere manter IsSafeForCompression como static.

O método IsSafeForCompression não utiliza nenhum campo de instância (_logger ou _next). Poderia permanecer static para clareza, a menos que haja planos de usar o logger dentro dele futuramente.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs`
around lines 49 - 77, The IsSafeForCompression method in
CompressionSecurityMiddleware does not access any instance fields and should be
declared static for clarity; change its signature to static bool
IsSafeForCompression(HttpContext context) and update any internal calls (e.g.,
from InvokeAsync or other methods in CompressionSecurityMiddleware) to call the
static method directly, ensuring there are no references to instance state
inside it and updating any unit tests or callers accordingly.

16-21: Logger injetado mas não utilizado.

O _logger é injetado no construtor mas não é utilizado em nenhum lugar do middleware. Considere:

  1. Adicionar logs para auditoria quando compressão é desabilitada (especialmente útil para debugging em produção)
  2. Ou remover a dependência se não for necessária
💡 Sugestão opcional para uso do logger
         if (!isSafe)
         {
+            _logger.LogDebug("Compression disabled for request to {Path} due to security policy", context.Request.Path);
             context.Request.Headers["Accept-Encoding"] = "identity";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs`
around lines 16 - 21, O campo _logger é injetado na classe
CompressionSecurityMiddleware (construtor CompressionSecurityMiddleware) mas não
está a ser usado; ou remova a dependência (_logger e o parâmetro ILogger no
construtor) ou passe a usá‑la no método InvokeAsync/Invoke para auditoria:
registe mensagens informativas/aviso quando a compressão for desabilitada (por
exemplo quando checa Accept-Encoding ou adiciona/retira headers de
Content-Encoding), inclua contexto relevante (Request.Path, razão da
desabilitação) e use níveis apropriados (Information/Warning) para facilitar
debugging em produção.
tests/MeAjudaAi.Integration.Tests/Middleware/CompressionSecurityMiddlewareTests.cs (1)

47-66: Considere verificar ausência do header X-Compression-Disabled.

Para usuários anônimos, seria útil também verificar que o header de debug X-Compression-Disabled não está presente, garantindo que o middleware não interferiu.

💡 Sugestão de assertion adicional
         // Assert
         response.StatusCode.Should().Be(HttpStatusCode.OK);
         
+        // Header de debug não deve estar presente para usuários anônimos
+        response.Headers.Should().NotContainKey("X-Compression-Disabled",
+            "Security middleware should not add debug header for anonymous users");
+        
         // For anonymous users, compression may be enabled
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@tests/MeAjudaAi.Integration.Tests/Middleware/CompressionSecurityMiddlewareTests.cs`
around lines 47 - 66, In the test
CompressionSecurity_AnonymousUser_ShouldAllowCompression, after asserting
StatusCode, add an assertion that the response does NOT contain the debug header
"X-Compression-Disabled" to ensure middleware didn't disable compression for
anonymous users; use the HttpResponseMessage (response) headers APIs (e.g.,
response.Headers.Contains("X-Compression-Disabled") or TryGetValues) to assert
false/empty. Keep the existing header cleanup
(HttpClient.DefaultRequestHeaders.Remove("Accept-Encoding")) as-is.
src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs (1)

600-607: Evite um extension method vazio com nome operacional.

UseSecurityHardening() não aplica nenhum middleware e hoje só devolve app. Isso passa a impressão de que o hardening está centralizado aqui, quando a ordem real continua espalhada em outro ponto do pipeline. Ou mova o registro efetivo para este método, ou renomeie-o para deixar claro que ele é apenas um placeholder.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs`
around lines 600 - 607, The extension method UseSecurityHardening currently does
nothing and misleads readers about where security hardening is applied; either
move the actual middleware registrations into this method (e.g., register
SecurityHeadersMiddleware and any other hardening middlewares here so the
pipeline order is centralized) or rename the method to an explicit placeholder
name (e.g., UseSecurityHardeningPlaceholder or UseNoOpSecurityHardening) and
update the XML comment to reflect that it intentionally does not register
middleware; adjust any callers (e.g., places that currently rely on
UseApiMiddlewares) to call the new implementation or keep behavior consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs`:
- Around line 568-570: Ajuste o fallback usado no cálculo de permitLimit para
alinhar com os patamares reais de AdvancedRateLimit (120 para autenticados, 30
para anônimos) em vez de 1000/100; localize o trecho que usa isAuthenticated e
configuration.GetValue("AdvancedRateLimit:Authenticated:RequestsPerMinute",
1000) / configuration.GetValue("AdvancedRateLimit:Anonymous:RequestsPerMinute",
100) (usado pelo GlobalLimiter) e altere os valores padrão para 120 e 30
respectivamente para manter comportamento consistente quando a seção
AdvancedRateLimit não estiver presente.
- Around line 298-299: A configuração atual apenas expõe o header antiforgery
via policy.WithExposedHeaders("X-XSRF-TOKEN") but não o inclui nos headers
permitidos do preflight; altere o trecho onde o CORS policy é construído
(referência: policy.WithExposedHeaders("X-XSRF-TOKEN") e à configuração
AllowedHeaders) para também adicionar "X-XSRF-TOKEN" aos headers permitidos
quando AllowedHeaders não for "*" — ou seja, ao aplicar a configuração de
AllowedHeaders, garanta que "X-XSRF-TOKEN" seja incluído (por exemplo
adicionando/mesclando com policy.WithHeaders(...) ou equivalente) para que o
preflight permita o header do SPA.

In `@tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`:
- Around line 414-443: The detection of Result/Response types in
ReadJsonAsync<T> is fragile; replace the current isResultType logic (which uses
GetGenericTypeDefinition().Name.StartsWith) with a robust check: if
typeof(T).IsGenericType compare GetGenericTypeDefinition().Name exactly to
"Result`1" or "Response`1" (or compare the definition to your concrete generic
type if available), and also add a branch to treat non-generic response wrapper
types by checking typeof(T).Name.EndsWith("Response") or a known namespace
(e.g., Contracts.Functional) so plain types like UploadDocumentResponse are
unwrapped; keep using the existing JSON inspection and keep error handling via
BuildSafeResponsePreview and SerializationDefaults.Api unchanged.

---

Nitpick comments:
In `@src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs`:
- Around line 600-607: The extension method UseSecurityHardening currently does
nothing and misleads readers about where security hardening is applied; either
move the actual middleware registrations into this method (e.g., register
SecurityHeadersMiddleware and any other hardening middlewares here so the
pipeline order is centralized) or rename the method to an explicit placeholder
name (e.g., UseSecurityHardeningPlaceholder or UseNoOpSecurityHardening) and
update the XML comment to reflect that it intentionally does not register
middleware; adjust any callers (e.g., places that currently rely on
UseApiMiddlewares) to call the new implementation or keep behavior consistent.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs`:
- Around line 49-77: The IsSafeForCompression method in
CompressionSecurityMiddleware does not access any instance fields and should be
declared static for clarity; change its signature to static bool
IsSafeForCompression(HttpContext context) and update any internal calls (e.g.,
from InvokeAsync or other methods in CompressionSecurityMiddleware) to call the
static method directly, ensuring there are no references to instance state
inside it and updating any unit tests or callers accordingly.
- Around line 16-21: O campo _logger é injetado na classe
CompressionSecurityMiddleware (construtor CompressionSecurityMiddleware) mas não
está a ser usado; ou remova a dependência (_logger e o parâmetro ILogger no
construtor) ou passe a usá‑la no método InvokeAsync/Invoke para auditoria:
registe mensagens informativas/aviso quando a compressão for desabilitada (por
exemplo quando checa Accept-Encoding ou adiciona/retira headers de
Content-Encoding), inclua contexto relevante (Request.Path, razão da
desabilitação) e use níveis apropriados (Information/Warning) para facilitar
debugging em produção.

In
`@src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs`:
- Line 44: Padronize o formato de correlationId entre
DocumentRejectedIntegrationEventHandler e
DocumentVerifiedIntegrationEventHandler; escolha uma das abordagens sugeridas e
atualize a criação do correlationId dentro do método que constrói o evento (em
DocumentRejectedIntegrationEventHandler) para usar o mesmo padrão que o outro
handler (por exemplo, substituir var correlationId = $"{templateKey}:..."; por
um prefixo descritivo
$"document_rejected:{integrationEvent.DocumentId}:{integrationEvent.ProviderId}"
ou, alternativamente, alterar DocumentVerifiedIntegrationEventHandler para usar
templateKey em vez do literal), garantindo que ambos handlers usem o mesmo
formato para idempotência e atualize quaisquer testes ou consumidores que
dependam do formato antigo.

In
`@tests/MeAjudaAi.Integration.Tests/Middleware/CompressionSecurityMiddlewareTests.cs`:
- Around line 47-66: In the test
CompressionSecurity_AnonymousUser_ShouldAllowCompression, after asserting
StatusCode, add an assertion that the response does NOT contain the debug header
"X-Compression-Disabled" to ensure middleware didn't disable compression for
anonymous users; use the HttpResponseMessage (response) headers APIs (e.g.,
response.Headers.Contains("X-Compression-Disabled") or TryGetValues) to assert
false/empty. Keep the existing header cleanup
(HttpClient.DefaultRequestHeaders.Remove("Accept-Encoding")) as-is.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: afb2776a-f4c4-4b43-99d0-62ece6a24ccf

📥 Commits

Reviewing files that changed from the base of the PR and between 0cf039f and 38aa1da.

⛔ Files ignored due to path filters (1)
  • src/Modules/Users/Infrastructure/Persistence/Migrations/UsersDbContextModelSnapshot.cs is excluded by !**/Migrations/**
📒 Files selected for processing (20)
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/AntiforgeryCookieMiddleware.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs
  • src/Contracts/Contracts/Modules/Communications/DTOs/OutboxPayloads.cs
  • src/Contracts/Shared/CommunicationTemplateKeys.cs
  • src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Handlers/DocumentVerifiedIntegrationEventHandler.cs
  • src/Modules/Communications/Application/Services/OutboxProcessorService.cs
  • src/Modules/Communications/Infrastructure/Persistence/Configurations/OutboxMessageConfiguration.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentVerifiedIntegrationEventHandlerTests.cs
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceActivatedDomainEventHandler.cs
  • src/Modules/Users/Tests/Integration/UserRepositoryIntegrationTests.cs
  • src/Shared/Database/Outbox/OutboxMessageConstraints.cs
  • src/Shared/Monitoring/BusinessMetrics.cs
  • tests/MeAjudaAi.ApiService.Tests/Unit/Middlewares/CompressionSecurityMiddlewareTests.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Middleware/CompressionSecurityMiddlewareTests.cs
  • tests/MeAjudaAi.Shared.Tests/TestInfrastructure/Base/BaseDatabaseTest.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Monitoring/BusinessMetricsTests.cs
✅ Files skipped from review due to trivial changes (3)
  • src/Shared/Database/Outbox/OutboxMessageConstraints.cs
  • src/Contracts/Shared/CommunicationTemplateKeys.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentVerifiedIntegrationEventHandlerTests.cs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/Shared/Monitoring/BusinessMetrics.cs
  • src/Modules/ServiceCatalogs/Infrastructure/Events/Handlers/ServiceActivatedDomainEventHandler.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/AntiforgeryCookieMiddleware.cs
  • src/Modules/Communications/Tests/Unit/Application/Handlers/DocumentRejectedIntegrationEventHandlerTests.cs

Comment thread src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs Outdated
Comment thread tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
- Robust type detection in BaseApiTest.ReadJsonAsync.
- Standardized correlationId format across integration handlers.
- Fixed CORS preflight to permit X-XSRF-TOKEN header.
- Standardized all logs to English.
- Cleaned up dead security hardening placeholder.
- Verified system stability with all 710+ tests passing (Unit, Integration, E2E).
@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs (1)

55-61: ⚠️ Potential issue | 🟠 Major

Cobertura de headers sensíveis incompleta no critério de segurança.

O middleware verifica apenas Authorization e X-API-Key (linhas 57-58). Embora a API não implemente autenticação por sessão/cookie, ela utiliza XSRF-TOKEN como proteção antiforgery. Se a requisição incluir o header X-XSRF-TOKEN (ou o cookie XSRF-TOKEN), ela contém dados sensíveis que devem evitar compressão para proteção BREACH/CRIME.

🔧 Ajuste sugerido
-        if (request.Headers.ContainsKey("Authorization") ||
-            request.Headers.ContainsKey("X-API-Key"))
+        if (request.Headers.ContainsKey("Authorization") ||
+            request.Headers.ContainsKey("X-API-Key") ||
+            request.Headers.ContainsKey("X-XSRF-TOKEN"))
         {
             return false;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs`
around lines 55 - 61, The compression security check in
CompressionSecurityMiddleware currently only inspects Authorization and
X-API-Key headers; update the check in the method that evaluates request
compression eligibility to also detect the anti-forgery token by checking for
the header "X-XSRF-TOKEN" and the cookie "XSRF-TOKEN" (i.e., treat requests
containing either request.Headers.ContainsKey("X-XSRF-TOKEN") or
request.Cookies.ContainsKey("XSRF-TOKEN") as authenticated/sensitive and return
false), ensuring the same early-return logic used for Authorization/X-API-Key
applies to these antiforgery indicators.
🧹 Nitpick comments (1)
src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs (1)

53-59: Centralize Subject/Body no template para evitar drift de conteúdo.

Como TemplateKey e TemplateData já estão presentes, manter Subject e Body hardcoded aqui aumenta risco de divergência entre handler e template versionado. Vale centralizar esses textos na camada de template e deixar o handler só com dados dinâmicos.

Based on learnings: "Consider consolidating translations via a centralized logger or localization keys to maintain consistency and ease future localization."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs`
around lines 53 - 59, The handler DocumentRejectedIntegrationEventHandler
currently sets Subject and Body inline when constructing EmailOutboxPayload,
which risks drift from the versioned template; remove the hardcoded Subject and
Body from the EmailOutboxPayload construction and instead rely solely on
TemplateKey and TemplateData (ensure templateData contains the dynamic values
like provider name, document type, and reason). Update the EmailOutboxPayload
invocation in the handler to omit Subject and Body, populate TemplateData keys
(e.g., ProviderName, DocumentType, Reason) expected by the centralized template,
and ensure the email template (referenced by templateKey) contains the
localized/templated subject and body. Ensure any downstream consumer of
EmailOutboxPayload handles missing Subject/Body or falls back to template
rendering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs`:
- Around line 55-61: The compression security check in
CompressionSecurityMiddleware currently only inspects Authorization and
X-API-Key headers; update the check in the method that evaluates request
compression eligibility to also detect the anti-forgery token by checking for
the header "X-XSRF-TOKEN" and the cookie "XSRF-TOKEN" (i.e., treat requests
containing either request.Headers.ContainsKey("X-XSRF-TOKEN") or
request.Cookies.ContainsKey("XSRF-TOKEN") as authenticated/sensitive and return
false), ensuring the same early-return logic used for Authorization/X-API-Key
applies to these antiforgery indicators.

---

Nitpick comments:
In
`@src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs`:
- Around line 53-59: The handler DocumentRejectedIntegrationEventHandler
currently sets Subject and Body inline when constructing EmailOutboxPayload,
which risks drift from the versioned template; remove the hardcoded Subject and
Body from the EmailOutboxPayload construction and instead rely solely on
TemplateKey and TemplateData (ensure templateData contains the dynamic values
like provider name, document type, and reason). Update the EmailOutboxPayload
invocation in the handler to omit Subject and Body, populate TemplateData keys
(e.g., ProviderName, DocumentType, Reason) expected by the centralized template,
and ensure the email template (referenced by templateKey) contains the
localized/templated subject and body. Ensure any downstream consumer of
EmailOutboxPayload handles missing Subject/Body or falls back to template
rendering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5a9f27ba-bd13-44f7-8cd3-2c05df1846d3

📥 Commits

Reviewing files that changed from the base of the PR and between 38aa1da and 26f8196.

📒 Files selected for processing (6)
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/SecurityExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/CompressionSecurityMiddleware.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
  • src/Modules/Communications/Application/Handlers/DocumentRejectedIntegrationEventHandler.cs
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
  • tests/MeAjudaAi.Integration.Tests/Middleware/CompressionSecurityMiddlewareTests.cs
✅ Files skipped from review due to trivial changes (1)
  • tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
  • tests/MeAjudaAi.Integration.Tests/Middleware/CompressionSecurityMiddlewareTests.cs

…rationTests to avoid constraint violations
@github-actions
Copy link
Copy Markdown

Code Coverage Report

Code Coverage

Project Package Line Rate Branch Rate Health
MeAjudaAi.Web.Admin components/layout 72% 64%
MeAjudaAi.Web.Admin components/providers 93% 92%
MeAjudaAi.Web.Admin components/ui 100% 86%
MeAjudaAi.Web.Admin hooks/admin 95% 79%
MeAjudaAi.Web.Admin Summary 94% (253 / 269) 80% (117 / 146) -
MeAjudaAi.Web.Customer components/auth 62% 57%
MeAjudaAi.Web.Customer components/home 100% 100%
MeAjudaAi.Web.Customer components/layout 63% 36%
MeAjudaAi.Web.Customer components/profile 93% 94%
MeAjudaAi.Web.Customer components/providers 61% 64%
MeAjudaAi.Web.Customer components/reviews 64% 70%
MeAjudaAi.Web.Customer components/search 88% 70%
MeAjudaAi.Web.Customer components/service 100% 100%
MeAjudaAi.Web.Customer components/ui 97% 97%
MeAjudaAi.Web.Customer hooks 88% 63%
MeAjudaAi.Web.Customer lib/api 83% 80%
MeAjudaAi.Web.Customer lib/constants 100% 100%
MeAjudaAi.Web.Customer lib/schemas 92% 80%
MeAjudaAi.Web.Customer lib/services 94% 86%
MeAjudaAi.Web.Customer lib/utils 77% 90%
MeAjudaAi.Web.Customer Summary 80% (660 / 816) 74% (576 / 770) -
MeAjudaAi.Web.Provider root 53% 36%
MeAjudaAi.Web.Provider components/dashboard 100% 100%
MeAjudaAi.Web.Provider components/layout 100% 100%
MeAjudaAi.Web.Provider components/profile 100% 100%
MeAjudaAi.Web.Provider components/providers 100% 100%
MeAjudaAi.Web.Provider components/ui 100% 90%
MeAjudaAi.Web.Provider lib/api 100% 93%
MeAjudaAi.Web.Provider lib/constants 100% 100%
MeAjudaAi.Web.Provider lib/schemas 87% 75%
MeAjudaAi.Web.Provider lib/services 100% 100%
MeAjudaAi.Web.Provider lib/utils 96% 98%
MeAjudaAi.Web.Provider Summary 86% (277 / 321) 78% (357 / 457) -
Overall Summary 84% (1190 / 1406) 76% (1050 / 1373) -

@github-actions
Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
MeAjudaAi.ApiService 87% 71% 443
MeAjudaAi.Modules.Communications.API 100% 100% 4
MeAjudaAi.Modules.Communications.Application 88% 62% 38
MeAjudaAi.Modules.Communications.Domain 100% 100% 17
MeAjudaAi.Modules.Communications.Infrastructure 98% 100% 20
MeAjudaAi.Modules.Documents.API 100% 100% 8
MeAjudaAi.Modules.Documents.Application 97% 49% 66
MeAjudaAi.Modules.Documents.Domain 99% 100% 32
MeAjudaAi.Modules.Documents.Infrastructure 91% 72% 98
MeAjudaAi.Modules.Locations.API 100% 100% 18
MeAjudaAi.Modules.Locations.Application 100% 100% 17
MeAjudaAi.Modules.Locations.Domain 95% 82% 128
MeAjudaAi.Modules.Locations.Infrastructure 93% 60% 41
MeAjudaAi.Modules.Providers.API 99% 100% 43
MeAjudaAi.Modules.Providers.Application 98% 62% 145
MeAjudaAi.Modules.Providers.Domain 90% 82% 288
MeAjudaAi.Modules.Providers.Infrastructure 100% 100% 25
MeAjudaAi.Modules.SearchProviders.API 100% 100% 3
MeAjudaAi.Modules.SearchProviders.Application 91% 54% 37
MeAjudaAi.Modules.SearchProviders.Domain 96% 82% 51
MeAjudaAi.Modules.SearchProviders.Infrastructure 99% 88% 40
MeAjudaAi.Modules.ServiceCatalogs.API 100% 100% 20
MeAjudaAi.Modules.ServiceCatalogs.Application 100% 100% 37
MeAjudaAi.Modules.ServiceCatalogs.Domain 96% 93% 70
MeAjudaAi.Modules.ServiceCatalogs.Infrastructure 96% 100% 9
MeAjudaAi.Modules.Users.API 100% 100% 20
MeAjudaAi.Modules.Users.Application 97% 73% 76
MeAjudaAi.Modules.Users.Domain 94% 95% 145
MeAjudaAi.Modules.Users.Infrastructure 90% 56% 94
MeAjudaAi.Shared 74% 67% 865
Summary 91% (8475 / 9323) 73% (1462 / 2004) 2898

Minimum allowed line rate is 90%

@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Apr 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@frigini frigini merged commit 783aae6 into master Apr 13, 2026
12 checks passed
@frigini frigini deleted the feature/sprint-9-buffer-communications branch April 13, 2026 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant