Skip to content

feat: Sprint 8B.2 Core Technical Excellence#151

Merged
frigini merged 12 commits intomasterfrom
feature/sprint-8b2-core-tech
Mar 10, 2026
Merged

feat: Sprint 8B.2 Core Technical Excellence#151
frigini merged 12 commits intomasterfrom
feature/sprint-8b2-core-tech

Conversation

@frigini
Copy link
Owner

@frigini frigini commented Mar 9, 2026

Part 1 of Sprint 8B.2 Technical Excellence. Splits out Docs, CI/CD, CodeRabbit Config, and Keycloak/Record Standardizations for easier review.

Summary by CodeRabbit

  • Documentation

    • Expanded current roadmap with phase plans, architecture, sprint timelines; added a long-term future roadmap and updated technical-debt tracking.
  • Chores

    • Added development Keycloak bootstrap automation and review-tool config to exclude generated/non-source files.
    • Consolidated monitoring and resilience registration into unified extension points.
    • Converted several DTOs to positional constructors and updated related code/tests (API signatures changed).
  • Style

    • Adjusted editorconfig suppressions to reduce refactor noise.
  • Security

    • Enforced explicit DB password configuration (removed default fallbacks).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

Important

Review skipped

This PR was authored by the user configured for CodeRabbit reviews. CodeRabbit does not review PRs authored by this user. It's recommended to use a dedicated user account to post CodeRabbit review feedback.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 26236f70-704b-4a9b-9420-67b26c3b7da5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Keycloak bootstrap BackgroundService and dev wiring; converts several Documents DTOs to positional records (breaking object-initializer usage) and updates callers/tests; consolidates business-metrics extension surface; enforces explicit DB passwords; resilience DI tweaks; large docs/config additions and dependency lockfile downgrades.

Changes

Cohort / File(s) Summary
Config & Docs
/.coderabbit.yaml, /.editorconfig, docs/roadmap-current.md, docs/roadmap-future.md, docs/technical-debt.md
Adds CI/analysis config and editorconfig suppressions; big roadmap and technical-debt document additions/rewrites.
Keycloak bootstrap
src/Aspire/MeAjudaAi.AppHost/Program.cs, src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs, src/Aspire/MeAjudaAi.AppHost/Options/MeAjudaAiKeycloakOptions.cs
Introduces KeycloakBootstrapService (hosted BackgroundService), registers it in Development, and adds options fields for resolved endpoints used during bootstrap.
Documents DTOs (public API)
src/Contracts/.../DocumentStatusCountDto.cs, src/Contracts/.../ModuleDocumentDto.cs, src/Contracts/.../ModuleDocumentStatusDto.cs
Refactors three DTOs from property-initialized records to positional-record constructors — public type signatures changed.
Documents API & Tests
src/Modules/Documents/Application/ModuleApi/DocumentsModuleApi.cs, tests/.../ModuleDocumentDtosTests.cs, tests/.../Pages/DocumentsPageTests.cs
Updates instantiations and tests to use new positional constructors instead of object initializers.
Monitoring / Middleware
src/Shared/Monitoring/BusinessMetricsMiddleware.cs, src/Shared/Monitoring/Extensions/BusinessMetricsExtensions.cs, src/Shared/Monitoring/MonitoringExtensions.cs
Removed older public extension files; consolidated AddBusinessMetrics/UseBusinessMetrics into MonitoringExtensions (new public methods added).
Resilience & DI changes
src/Web/MeAjudaAi.Web.Admin/Extensions/ServiceCollectionExtensions.cs
Refactors HTTP resilience configuration to use pipeline-bound options and DI-provided ILogger when building policies.
DB / Permissions tightening
src/Shared/Database/BaseDesignTimeDbContextFactory.cs, src/Shared/Database/DatabaseExtensions.cs, src/Shared/Database/SchemaPermissionsManager.cs
Enforces explicit passwords from env/config (removes hardcoded defaults), removes default parameters, adds validation/exceptions, and converts some logs to English.
Lockfile / Dependencies
tests/.../packages.lock.json
Downgrades multiple dependencies (e.g., MudBlazor, Microsoft.Extensions.*) to earlier versions.
Other
/.editorconfig (suppression edits)
Removes some global suppressions and adds a temporary CS0105 suppression comment for duplicate using directives.

Sequence Diagram(s)

sequenceDiagram
    participant AppHost as AppHost (Startup)
    participant KBS as KeycloakBootstrapService
    participant RNS as ResourceNotificationService
    participant Keycloak as Keycloak Resource
    participant TokenEP as Keycloak Token Endpoint
    participant ClientsEP as Keycloak Clients API

    AppHost->>KBS: Register hosted service (Dev only)
    KBS->>RNS: WatchAsync()
    RNS->>KBS: Notify resource state changes
    RNS->>Keycloak: (resource reaches Running)
    KBS->>KBS: Delay 15s
    KBS->>TokenEP: POST /realms/master/protocol/openid-connect/token (admin-cli creds)
    TokenEP-->>KBS: access_token
    KBS->>ClientsEP: GET /clients?clientId=admin-portal
    alt client not found
        KBS->>ClientsEP: POST /clients (create admin-portal)
    end
    KBS->>ClientsEP: GET /clients?clientId=customer-web
    alt client not found
        KBS->>ClientsEP: POST /clients (create customer-web)
    end
    KBS-->>AppHost: Log bootstrap complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

🐇
Pulo entre branches, código a alinhar,
Registros viram construtores prontos a saltar,
Keycloak desperta, clientes vêm habitar,
Métricas congregam, pipelines a entoar,
Um coelho contente volta ao merge a celebrar.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal and lacks most required template sections (Problem, Solution, Changes detail, Impact, Testing, Checklist, Notes). While it provides basic context that this is Part 1 of Sprint 8B.2, it does not follow the provided template structure. Expand the description following the template to include: detailed Problem statement, Solution approach, comprehensive Changes list, expected Impact, Testing evidence (unit/integration tests), completed Checklist items, and Notes for Reviewers.
Docstring Coverage ⚠️ Warning Docstring coverage is 34.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main objective: implementing core technical excellence improvements for Sprint 8B.2, which aligns with the comprehensive changes across configuration, documentation, Keycloak bootstrapping, and record standardizations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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: 12

Caution

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

⚠️ Outside diff range comments (1)
docs/technical-debt.md (1)

3-3: ⚠️ Potential issue | 🟡 Minor

Keep this file “pending-only” or relax the contract at Line 3.

The new OTIMIZADO/resolved sections conflict with the opening rule that this document tracks only pending debt and removes completed items. That will make future maintenance ambiguous. Either move resolved work to a changelog/roadmap doc, or update Line 3 so the file explicitly allows both pending and resolved summaries.

Also applies to: 76-79, 163-169

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

In `@docs/technical-debt.md` at line 3, The opening sentence "Este documento
rastreia **apenas débitos técnicos PENDENTES**." conflicts with added
"OTIMIZADO"/resolved sections (e.g., the entries labeled "OTIMIZADO" and the
blocks around the comments referencing lines 76-79 and 163-169); either
remove/move all resolved "OTIMIZADO" entries out of this file into a
changelog/roadmap document and keep the opening sentence as-is, or update the
opening sentence to explicitly allow both pending and resolved summaries (and
adjust any headings or status labels to match the chosen contract). Ensure all
instances of the "OTIMIZADO" label are handled consistently across the document.
🧹 Nitpick comments (4)
.editorconfig (2)

55-55: Track removal plan for temporary suppression.

The CS0105 suppression is described as temporary ("avoid noise while refactoring Shared"), but there's no tracking mechanism or follow-up plan. Temporary suppressions risk becoming permanent technical debt.

Consider adding a TODO comment with a removal target (e.g., end of sprint, post-refactor) or creating a tracking issue.

♻️ Add removal tracking
-dotnet_diagnostic.CS0105.severity = none # Duplicate using directive: Suppressed to avoid noise while refactoring Shared
+dotnet_diagnostic.CS0105.severity = none # TODO(Sprint 8B.2): Remove after Shared refactoring complete - Duplicate using directive suppressed temporarily
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.editorconfig at line 55, The suppression for CS0105
(dotnet_diagnostic.CS0105.severity = none) is marked temporary but lacks a
removal plan—add a clear tracking note: update the .editorconfig line to include
a TODO comment indicating the removal target (e.g., sprint end or post-refactor
date) and/or reference a tracking issue ID or URL; also create the corresponding
issue in the tracker (or confirm an existing one) and put that issue number/URL
in the comment so reviewers can verify the follow-up.

54-54: Prefer file-specific suppression over global scope.

The S3267 suppression (loop simplification) is justified for "middleware and infrastructure loops" but applies globally to all C# files. This prevents catching legitimate cases elsewhere where LINQ would improve readability and maintainability.

Consider adding file-specific suppressions (e.g., [src/*/Middleware/**.cs]) or applying #pragma directives locally to the affected methods.

♻️ Example of file-specific suppression
-# Technical Excellence Sprint Suppressions (Avoiding source code pollution)
-dotnet_diagnostic.S2068.severity = none # HardcodedPasswordCheck: Suppressed for messaging constants and test connection strings
-dotnet_diagnostic.S3267.severity = none # Loop should be simplified by calling LINQ: Suppressed for middleware and infrastructure loops
-dotnet_diagnostic.CS0105.severity = none # Duplicate using directive: Suppressed to avoid noise while refactoring Shared
+# Technical Excellence Sprint Suppressions (Avoiding source code pollution)
+dotnet_diagnostic.S2068.severity = none # HardcodedPasswordCheck: Suppressed for messaging constants and test connection strings
+dotnet_diagnostic.CS0105.severity = none # Duplicate using directive: Suppressed to avoid noise while refactoring Shared
+
+# Middleware-specific rules
+[src/*/Middleware/**.cs]
+dotnet_diagnostic.S3267.severity = none # Loop simplification not applicable in middleware initialization
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.editorconfig at line 54, The global suppression of dotnet_diagnostic.S3267
in .editorconfig is too broad; replace it with file-scoped suppression sections
or move to local pragmas: remove or narrow the line
"dotnet_diagnostic.S3267.severity = none" from the global section and instead
add file-specific entries (e.g., per-folder patterns targeting your
middleware/infrastructure files) or add `#pragma warning disable S3267`/`#pragma
warning restore S3267` around the specific methods/classes that need it; search
for S3267 in the repo and update .editorconfig and/or the affected source files
to limit the suppression scope.
docs/technical-debt.md (1)

91-94: Version pinning in debt entry is accurate but consider removing it or establishing a sync process.

The Aspire.Hosting.Keycloak version documented (v13.1.0-preview) matches the current package metadata in the repository (locked to 13.1.0-preview.1.25616.3 in AppHost). However, embedding exact versions in the technical debt tracker creates future maintenance risk. When the package advances to stable, this entry will become misleading unless actively updated.

Recommend either:

  • Remove the version entirely ("Aspire.Hosting.Keycloak (preview)") to focus on the actual limitation (lack of real health checks), or
  • Establish a review process to update version references when package versions change.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/technical-debt.md` around lines 91 - 94, The technical-debt entry "🚀
[ISSUE `#112`] tech: aguardar versão stable do Aspire.Hosting.Keycloak" currently
pins "Aspire.Hosting.Keycloak (v13.1.0-preview)"; remove the exact version
string and replace it with a non-pinned label such as "Aspire.Hosting.Keycloak
(preview)" in that heading/content to avoid stale metadata, and add a one-line
follow-up action recommending a periodic review process to update the entry when
the package reaches stable (or alternatively document steps to re-evaluate the
debt on package version changes).
src/Aspire/MeAjudaAi.AppHost/Program.cs (1)

15-19: Only register the bootstrapper when Keycloak is actually part of the topology.

ConfigureTestingEnvironment never provisions Keycloak and also disables it for the API at Line 93, so this hosted service cannot observe a keycloak resource in tests. Restricting the registration to Development for now keeps the AppHost startup path simpler; if a future test topology adds Keycloak, wire the service next to that resource registration.

♻️ Suggested change
-        if (EnvironmentHelpers.IsDevelopment(builder) || EnvironmentHelpers.IsTesting(builder))
+        if (EnvironmentHelpers.IsDevelopment(builder))
         {
             builder.Services.AddHostedService<KeycloakBootstrapService>();
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Aspire/MeAjudaAi.AppHost/Program.cs` around lines 15 - 19, The hosted
Keycloak bootstrap service is being registered for both Development and Testing
but tests never provision Keycloak; update the registration so
KeycloakBootstrapService is only added when
EnvironmentHelpers.IsDevelopment(builder) is true (remove
EnvironmentHelpers.IsTesting(builder)) or alternatively gate registration with
the same Keycloak-enabled flag used in ConfigureTestingEnvironment; adjust the
conditional around builder.Services.AddHostedService<KeycloakBootstrapService>()
accordingly so the service is not registered in test topologies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.editorconfig:
- Line 53: The global S2068 suppression in .editorconfig hides hardcoded
credentials and must be removed; replace the hardcoded secrets by reading them
from secure configuration or environment/secret store (e.g., Azure Key Vault,
AWS Secrets Manager) and update the impacted symbols: change default parameters
"users_secret" and "app_secret" in SchemaPermissionsManager
(src/Shared/Database/SchemaPermissionsManager.cs) to obtain values from
configuration, remove the literal password "dev123" in
BaseDesignTimeDbContextFactory by loading the connection password from
env/config, and extract credentials from RabbitMqOptions connection strings to
secure config; after fixing production code, if any test fixtures legitimately
need dummy literals, add localized `#pragma` warning disable S2068 with an inline
justification only in those test files.

In `@docs/roadmap-current.md`:
- Around line 444-453: Update the "## 🔥 Tarefas Técnicas Cross-Module ⏳
ATUALIZADO" Sprint 5.5 status block so it reflects the later timeline and
completed work (replace "⏳ Aguardando validação CI/CD antes do merge" and "📋
Desenvolvimento frontend aguardando conclusão desta sprint" with
completed/merged status); specifically change the status to indicate CI/CD
validation passed and PRs merged on 19 Dez 2025 and mark frontend development as
completed or moved to next tasks so the block matches the later notes described
around lines 547-572; edit the text under that heading to use past-tense
descriptors (e.g., "✅ CI/CD validado e merges concluídos em 19 Dez 2025", "✅
Desenvolvimento frontend concluído") referencing the existing heading/title so
the update is made in the correct section.
- Around line 223-365: The roadmap section currently claims "CONCLUÍDO /
IMPLEMENTADO" in the heading for "Fase 2: Database-Backed + Admin Portal UI
(CONCLUÍDO - Sprint 7, 7 Jan 2026)" but still includes unchecked tasks, refactor
notes (GeographicRestrictionOptions, IAllowedRegionsService,
GeographicRestrictionMiddleware) and a Sprint 3 migration plan; update the
document by splitting it into two clear subsections: (1) "Shipped on 7 Jan 2026"
listing only the concrete, completed items (e.g., AllowedCities UI completed
with CRUD, coordinates, service radius and any DB schema migrations that were
actually applied such as geographic_restrictions.allowed_regions), and (2)
"Follow-up / Planned Improvements" containing remaining checkboxes, the refactor
guidance (GeographicRestrictionOptions deprecation, IAllowedRegionsService,
middleware refactor), migration path, and tests; ensure the header is changed to
reflect completed status only for shipped work and move all unchecked checklist
items and “Abordagem 1/2” content into the follow-up section so the roadmap
accurately reflects what shipped versus what remains.
- Around line 1-1202: The file contains mojibake (e.g. tokens like "🎨",
"CONCLUÍDA", "dinâmica", "├──") due to wrong encoding; reopen the
document with correct UTF-8 decoding (or detect the original encoding such as
windows-1252/ISO-8859-1 and re-decode to UTF-8), restore/replace corrupted
sequences with their proper characters (e.g. "Fase 2", "CONCLUÍDA", "dinâmica",
the src/ tree layout), or revert to the last known-good commit for this file and
re-save it explicitly as UTF-8, then run a quick grep for the unique strings
(GeographicRestrictionOptions, GeographicRestrictionMiddleware,
AllowedCities/AllowedStates, src/ tree markers like Γö£ΓöÇΓöÇ) to verify all
instances are fixed and commit the corrected UTF-8 file.
- Around line 179-184: The markdown fenced code block containing
"FeatureManagement:GeographicRestriction = true … allowed_regions.is_active =
true" is unlabeled causing MD040; add a language tag (e.g., `text`) after the
opening triple backticks so the block reads ```text, keeping the same content
and closing ``` to satisfy markdownlint MD040 while preserving the illustrative
config flow.

In `@docs/roadmap-future.md`:
- Line 1: The file contains mojibake in the Markdown heading ("### 🔮 **Baixa
Prioridade (12+ meses - Fase 3)**"); re-save the file using UTF-8 encoding and
restore the original characters for that heading (replace the garbled "🔮"
with the intended symbol/text). Open the document in an editor that lets you
change file encoding (or re-import with the correct encoding), convert/save as
UTF-8 without BOM, verify the heading displays correctly, and commit the fixed
file.
- Around line 49-51: The footer in the roadmap-future document contains stale
timeline strings ("Sprint 8B ✅ CONCLUÍDO" and "MVP Launch em 28 de Março de
2026"); update those footer lines to match the active Sprint 8B.2 plan (replace
the "Sprint 8B" status with "Sprint 8B.2 — em progresso" or equivalent and
change the MVP launch date to "May 12-16, 2026" so the footer text matches the
current roadmap content that marks Sprint 8B.2 in progress and the May MVP
window).

In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs`:
- Around line 71-72: The code currently hardcodes frontend redirect roots when
calling EnsureClientExistsAsync for "admin-portal" and "customer-web"; instead,
change the KeycloakBootstrapService to accept the real frontend base URLs via
its constructor or a configuration model that is populated by the same
registration that calls WithExternalHttpEndpoints() in Program.cs, then use
those injected values when calling EnsureClientExistsAsync (and similarly
replace the other hardcoded instances around the EnsureClientExistsAsync calls
at the later occurrences). Update the service constructor and usages so
EnsureClientExistsAsync(httpClient, "admin-portal", adminPortalBaseUrl, ct) and
EnsureClientExistsAsync(httpClient, "customer-web", customerWebBaseUrl, ct) are
fed from the shared config/registration rather than hardcoded
"http://localhost:5030" / "http://localhost:3000".
- Around line 11-14: The XML summary and inline comments in
KeycloakBootstrapService (class KeycloakBootstrapService and any
methods/constructors inside it) are in English and should be translated to
Portuguese; update all /// <summary>, /// <param>, /// <returns> and inline //
comments within KeycloakBootstrapService.cs to Portuguese while leaving runtime
log strings (e.g., any calls to logger.LogInformation/Error/Warning) in English
unchanged, and maintain original meaning and technical terms (OIDC, Keycloak,
admin-portal, customer-web) in the translations.
- Around line 41-44: The loop is breaking even when BootstrapKeycloakAsync logs
failures; change BootstrapKeycloakAsync to return a bool (or throw on fatal
error) indicating confirmed success, then only break the watcher loop when that
boolean is true; update all call sites where the code currently awaits
BootstrapKeycloakAsync(...) and immediately breaks (including the other similar
occurrences) to check the returned success flag before breaking or announcing
success, ensuring transient failures do not stop further retries.
- Around line 31-50: The catch-all around the bootstrap loop treats normal
shutdown cancellation as an error; update the exception handling in
KeycloakBootstrapService (the block that calls BootstrapKeycloakAsync and uses
stoppingToken) to detect OperationCanceledException (or when
stoppingToken.IsCancellationRequested) and either rethrow or quietly exit the
loop instead of logging an error; only log logger.LogError for non-cancellation
exceptions (keep BootstrapKeycloakAsync and stoppingToken usage unchanged).

In `@src/Contracts/Contracts/Modules/Documents/DTOs/ModuleDocumentStatusDto.cs`:
- Around line 6-8: Update the XML documentation on ModuleDocumentStatusDto to
match the actual enum values used by EDocumentStatus: change the Status
docstring to list the PascalCase values (e.g., "Uploaded", "Pending" or
"PendingVerification" as appropriate, "Verified", "Rejected") and ensure the
parameter tags for DocumentId and UpdatedAt remain accurate; reference
ModuleDocumentStatusDto and EDocumentStatus so the doc comment aligns with the
real statuses used across the codebase.

---

Outside diff comments:
In `@docs/technical-debt.md`:
- Line 3: The opening sentence "Este documento rastreia **apenas débitos
técnicos PENDENTES**." conflicts with added "OTIMIZADO"/resolved sections (e.g.,
the entries labeled "OTIMIZADO" and the blocks around the comments referencing
lines 76-79 and 163-169); either remove/move all resolved "OTIMIZADO" entries
out of this file into a changelog/roadmap document and keep the opening sentence
as-is, or update the opening sentence to explicitly allow both pending and
resolved summaries (and adjust any headings or status labels to match the chosen
contract). Ensure all instances of the "OTIMIZADO" label are handled
consistently across the document.

---

Nitpick comments:
In @.editorconfig:
- Line 55: The suppression for CS0105 (dotnet_diagnostic.CS0105.severity = none)
is marked temporary but lacks a removal plan—add a clear tracking note: update
the .editorconfig line to include a TODO comment indicating the removal target
(e.g., sprint end or post-refactor date) and/or reference a tracking issue ID or
URL; also create the corresponding issue in the tracker (or confirm an existing
one) and put that issue number/URL in the comment so reviewers can verify the
follow-up.
- Line 54: The global suppression of dotnet_diagnostic.S3267 in .editorconfig is
too broad; replace it with file-scoped suppression sections or move to local
pragmas: remove or narrow the line "dotnet_diagnostic.S3267.severity = none"
from the global section and instead add file-specific entries (e.g., per-folder
patterns targeting your middleware/infrastructure files) or add `#pragma warning
disable S3267`/`#pragma warning restore S3267` around the specific
methods/classes that need it; search for S3267 in the repo and update
.editorconfig and/or the affected source files to limit the suppression scope.

In `@docs/technical-debt.md`:
- Around line 91-94: The technical-debt entry "🚀 [ISSUE `#112`] tech: aguardar
versão stable do Aspire.Hosting.Keycloak" currently pins
"Aspire.Hosting.Keycloak (v13.1.0-preview)"; remove the exact version string and
replace it with a non-pinned label such as "Aspire.Hosting.Keycloak (preview)"
in that heading/content to avoid stale metadata, and add a one-line follow-up
action recommending a periodic review process to update the entry when the
package reaches stable (or alternatively document steps to re-evaluate the debt
on package version changes).

In `@src/Aspire/MeAjudaAi.AppHost/Program.cs`:
- Around line 15-19: The hosted Keycloak bootstrap service is being registered
for both Development and Testing but tests never provision Keycloak; update the
registration so KeycloakBootstrapService is only added when
EnvironmentHelpers.IsDevelopment(builder) is true (remove
EnvironmentHelpers.IsTesting(builder)) or alternatively gate registration with
the same Keycloak-enabled flag used in ConfigureTestingEnvironment; adjust the
conditional around builder.Services.AddHostedService<KeycloakBootstrapService>()
accordingly so the service is not registered in test topologies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 687ef2f7-d19e-41b2-8fe6-59eeadf579ff

📥 Commits

Reviewing files that changed from the base of the PR and between 728370e and 399b708.

📒 Files selected for processing (19)
  • .coderabbit.yaml
  • .editorconfig
  • docs/roadmap-current.md
  • docs/roadmap-future.md
  • docs/roadmap-history.md
  • docs/roadmap.md
  • docs/technical-debt.md
  • src/Aspire/MeAjudaAi.AppHost/Program.cs
  • src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs
  • src/Contracts/Contracts/Modules/Documents/DTOs/DocumentStatusCountDto.cs
  • src/Contracts/Contracts/Modules/Documents/DTOs/ModuleDocumentDto.cs
  • src/Contracts/Contracts/Modules/Documents/DTOs/ModuleDocumentStatusDto.cs
  • src/Modules/Documents/Application/ModuleApi/DocumentsModuleApi.cs
  • src/Shared/Monitoring/BusinessMetricsMiddleware.cs
  • src/Shared/Monitoring/Extensions/BusinessMetricsExtensions.cs
  • src/Shared/Monitoring/MonitoringExtensions.cs
  • src/Web/MeAjudaAi.Web.Admin/Extensions/ServiceCollectionExtensions.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Contracts/DTOs/ModuleDocumentDtosTests.cs
  • tests/MeAjudaAi.Web.Admin.Tests/Pages/DocumentsPageTests.cs
💤 Files with no reviewable changes (2)
  • src/Shared/Monitoring/BusinessMetricsMiddleware.cs
  • src/Shared/Monitoring/Extensions/BusinessMetricsExtensions.cs

Comment on lines +6 to +8
/// <param name="DocumentId">ID do documento.</param>
/// <param name="Status">Status atual do documento. Valid values: "pending", "processing", "completed", "failed"</param>
/// <param name="UpdatedAt">Data da última atualização.</param>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Documentation mismatch: Status values are incorrect.

The XML doc lists "pending", "processing", "completed", "failed" but the actual status values used throughout the codebase are "Uploaded", "Pending" (or "PendingVerification"), "Verified", and "Rejected" (PascalCase, matching the EDocumentStatus enum).

📝 Proposed fix
-/// <param name="Status">Status atual do documento. Valid values: "pending", "processing", "completed", "failed"</param>
+/// <param name="Status">Status atual do documento (Uploaded, PendingVerification, Verified, Rejected).</param>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <param name="DocumentId">ID do documento.</param>
/// <param name="Status">Status atual do documento. Valid values: "pending", "processing", "completed", "failed"</param>
/// <param name="UpdatedAt">Data da última atualização.</param>
/// <param name="DocumentId">ID do documento.</param>
/// <param name="Status">Status atual do documento (Uploaded, PendingVerification, Verified, Rejected).</param>
/// <param name="UpdatedAt">Data da última atualização.</param>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Contracts/Contracts/Modules/Documents/DTOs/ModuleDocumentStatusDto.cs`
around lines 6 - 8, Update the XML documentation on ModuleDocumentStatusDto to
match the actual enum values used by EDocumentStatus: change the Status
docstring to list the PascalCase values (e.g., "Uploaded", "Pending" or
"PendingVerification" as appropriate, "Verified", "Rejected") and ensure the
parameter tags for DocumentId and UpdatedAt remain accurate; reference
ModuleDocumentStatusDto and EDocumentStatus so the doc comment aligns with the
real statuses used across the codebase.

Address CodeRabbit review feedback from PR #151:

- Fix roadmap UTF-8 encoding (mojibake) and formatting.

- Refine .editorconfig suppressions (removal of global S2068/S3267, update CS0105).

- Extract hardcoded AppHost Keycloak frontend URLs to configuration.

- Translate and improve KeycloakBootstrapService loop resilience.

- Avoid KeycloakBootstrapService startup in Testing environment.

- Extract hardcoded default DB passwords in SchemaPermissionsManager and Factories.

- Update XML summaries in ModuleDocumentStatusDto matching EDocumentStatus.
Copy link
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 (1)
src/Shared/Database/SchemaPermissionsManager.cs (1)

15-39: ⚠️ Potential issue | 🟡 Minor

Translate the emitted log messages in this method to English.

The new signature is fine, but the runtime logs in this touched method are still Portuguese (Configurando..., Permissões configuradas..., Erro ao configurar...). Please keep logs in English and leave comments/XML docs in Portuguese.

Based on learnings: In all C# files, keep logging messages in English, but ensure comments and XML documentation (///

, /// , etc.) are written in Portuguese across the codebase.

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

In `@src/Shared/Database/SchemaPermissionsManager.cs` around lines 15 - 39, Update
the log messages inside EnsureUsersModulePermissionsAsync to English: replace
logger.LogInformation("Configurando permissões...") with an English equivalent
(e.g., "Configuring permissions for Users module using existing scripts"),
change logger.LogInformation("✅ Permissões configuradas com sucesso para módulo
Users") to something like "✅ Permissions successfully configured for Users
module", and change logger.LogError(ex, "❌ Erro ao configurar permissões para
módulo Users") to an English error message (e.g., "❌ Error configuring
permissions for Users module"); leave comments/XML docs in Portuguese and keep
calls to ExecuteSchemaScript unchanged.
🧹 Nitpick comments (2)
src/Shared/Database/BaseDesignTimeDbContextFactory.cs (1)

83-93: Update XML documentation to reflect the environment variable requirement.

The XML summary at lines 83-85 doesn't mention that DB_PASSWORD is now required. Developers encountering the exception might not realize they can alternatively configure DefaultConnection in their appsettings files to avoid this code path entirely.

Consider updating the summary to clarify the behavior:

📝 Suggested documentation update
     /// <summary>
-    /// Obtém a string de conexão padrão para desenvolvimento local
+    /// Obtém a string de conexão padrão para desenvolvimento local.
+    /// Requer a variável de ambiente DB_PASSWORD.
+    /// Alternativa: configure "DefaultConnection" em appsettings.json ou appsettings.Development.json.
     /// </summary>
     protected virtual string GetDefaultConnectionString()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Database/BaseDesignTimeDbContextFactory.cs` around lines 83 - 93,
Update the XML summary for GetDefaultConnectionString to state that the method
reads DB_PASSWORD from the environment and will throw if not set (and that an
alternative is to provide DefaultConnection in appsettings to avoid this path);
specifically reference the DB_PASSWORD env var and the
GetDefaultConnectionString method and mention DefaultConnection as the alternate
configuration option so developers know how to avoid the exception thrown in
GetDefaultConnectionString.
src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs (1)

109-112: Consider loading admin credentials from configuration.

The admin credentials are hardcoded. While this service only runs in Development, the values should match what's configured in ConfigureDevelopmentEnvironment (lines 135-136 in Program.cs). Consider reading from IConfiguration for consistency.

♻️ Suggested improvement
+            var adminUsername = configuration["Keycloak:AdminUsername"] ?? "admin";
+            var adminPassword = configuration["Keycloak:AdminPassword"] ?? "admin123";
+
             Content = new FormUrlEncodedContent(
             [
                 new KeyValuePair<string, string>("client_id", "admin-cli"),
-                new KeyValuePair<string, string>("username", "admin"),      // Configuração dev local
-                new KeyValuePair<string, string>("password", "admin123"),   // Configuração dev local
+                new KeyValuePair<string, string>("username", adminUsername),
+                new KeyValuePair<string, string>("password", adminPassword),
                 new KeyValuePair<string, string>("grant_type", "password")
             ])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs` around
lines 109 - 112, Replace the hardcoded admin credentials in
KeycloakBootstrapService with values read from IConfiguration: inject
IConfiguration into the KeycloakBootstrapService constructor and use
configuration keys (e.g. "Keycloak:AdminUsername" and "Keycloak:AdminPassword")
when building the form data instead of the literal "admin"/"admin123"; ensure
this still only runs in Development (matching ConfigureDevelopmentEnvironment in
Program.cs) and provide sensible defaults or throw a clear error if the config
values are missing.
🤖 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/Database/DatabaseExtensions.cs`:
- Around line 106-110: The code currently uses the null-coalescing operator for
usersRolePassword and appRolePassword which only rejects null but allows empty
strings; update the logic in DatabaseExtensions.cs around the usersRolePassword
and appRolePassword assignments so that you treat empty or whitespace values as
missing: read configuration["Postgres:UsersRolePassword"] and
configuration["Postgres:AppRolePassword"] into the variables only if they are
not null/empty/whitespace (use string.IsNullOrWhiteSpace checks) and throw
InvalidOperationException with the same message when the resulting password is
null/empty/whitespace to reject blank config values.

In `@src/Shared/Database/SchemaPermissionsManager.cs`:
- Around line 15-18: Both public APIs (EnsureUsersModulePermissionsAsync and the
similar EnsureRolesModulePermissionsAsync) accept empty or whitespace role
passwords; add immediate fail-fast guards that validate usersRolePassword and
appRolePassword (and the corresponding role password parameter in the other
method) using string.IsNullOrWhiteSpace and throw an ArgumentException (or
ArgumentNullException) with a clear message identifying the invalid parameter if
validation fails so you never proceed to create roles or build connection
strings with blank credentials.

---

Outside diff comments:
In `@src/Shared/Database/SchemaPermissionsManager.cs`:
- Around line 15-39: Update the log messages inside
EnsureUsersModulePermissionsAsync to English: replace
logger.LogInformation("Configurando permissões...") with an English equivalent
(e.g., "Configuring permissions for Users module using existing scripts"),
change logger.LogInformation("✅ Permissões configuradas com sucesso para módulo
Users") to something like "✅ Permissions successfully configured for Users
module", and change logger.LogError(ex, "❌ Erro ao configurar permissões para
módulo Users") to an English error message (e.g., "❌ Error configuring
permissions for Users module"); leave comments/XML docs in Portuguese and keep
calls to ExecuteSchemaScript unchanged.

---

Nitpick comments:
In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs`:
- Around line 109-112: Replace the hardcoded admin credentials in
KeycloakBootstrapService with values read from IConfiguration: inject
IConfiguration into the KeycloakBootstrapService constructor and use
configuration keys (e.g. "Keycloak:AdminUsername" and "Keycloak:AdminPassword")
when building the form data instead of the literal "admin"/"admin123"; ensure
this still only runs in Development (matching ConfigureDevelopmentEnvironment in
Program.cs) and provide sensible defaults or throw a clear error if the config
values are missing.

In `@src/Shared/Database/BaseDesignTimeDbContextFactory.cs`:
- Around line 83-93: Update the XML summary for GetDefaultConnectionString to
state that the method reads DB_PASSWORD from the environment and will throw if
not set (and that an alternative is to provide DefaultConnection in appsettings
to avoid this path); specifically reference the DB_PASSWORD env var and the
GetDefaultConnectionString method and mention DefaultConnection as the alternate
configuration option so developers know how to avoid the exception thrown in
GetDefaultConnectionString.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78d02b2b-6609-4dfc-b373-63e695ff0375

📥 Commits

Reviewing files that changed from the base of the PR and between 399b708 and c87e9a6.

📒 Files selected for processing (11)
  • .editorconfig
  • docs/roadmap-current.md
  • docs/roadmap-future.md
  • docs/technical-debt.md
  • src/Aspire/MeAjudaAi.AppHost/Program.cs
  • src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs
  • src/Contracts/Contracts/Modules/Documents/DTOs/ModuleDocumentStatusDto.cs
  • src/Shared/Database/BaseDesignTimeDbContextFactory.cs
  • src/Shared/Database/DatabaseExtensions.cs
  • src/Shared/Database/SchemaPermissionsManager.cs
  • tests/MeAjudaAi.Web.Admin.Tests/packages.lock.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • .editorconfig
  • docs/roadmap-future.md
  • src/Contracts/Contracts/Modules/Documents/DTOs/ModuleDocumentStatusDto.cs

Apply additional fixes from PR #151 CodeRabbit inline and outline comments:

- Enforce string.IsNullOrWhiteSpace checks in DatabaseExtensions.cs for role passwords.

- Add ArgumentException guards for role passwords in SchemaPermissionsManager.cs.

- Translate log messages to English in SchemaPermissionsManager.cs.

- Extract Keycloak admin credentials in KeycloakBootstrapService.cs to IConfiguration checks.

- Document GetDefaultConnectionString environment variables in BaseDesignTimeDbContextFactory.cs XML docs.
Copy link
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)
src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs (1)

89-94: ⚠️ Potential issue | 🟠 Major

Propagate client-creation failures before returning success.

EnsureClientExistsAsync only logs POST failures, so BootstrapKeycloakAsync still logs success and returns true at Lines 93-94. One transient failure here stops the watcher permanently and leaves the clients unprovisioned.

🔁 Minimal fix
-            await EnsureClientExistsAsync(httpClient, "admin-portal", adminUrl, ct);
-            await EnsureClientExistsAsync(httpClient, "customer-web", customerUrl, ct);
-            
-            logger.LogInformation("Keycloak bootstrap completed successfully.");
-            return true;
+            var adminOk = await EnsureClientExistsAsync(httpClient, "admin-portal", adminUrl, ct);
+            var customerOk = await EnsureClientExistsAsync(httpClient, "customer-web", customerUrl, ct);
+
+            if (adminOk && customerOk)
+            {
+                logger.LogInformation("Keycloak bootstrap completed successfully.");
+                return true;
+            }
+
+            return false;
-    private async Task EnsureClientExistsAsync(HttpClient httpClient, string clientId, string baseUrl, CancellationToken ct)
+    private async Task<bool> EnsureClientExistsAsync(HttpClient httpClient, string clientId, string baseUrl, CancellationToken ct)
     {
         // Verificar se existe
         var getResponse = await httpClient.GetAsync($"/admin/realms/MeAjudaAi/clients?clientId={clientId}", ct);
         if (getResponse.IsSuccessStatusCode)
         {
@@
             if (clients != null && clients.Count > 0)
             {
                 logger.LogInformation("Client {ClientId} already exists.", clientId);
-                return;
+                return true;
             }
         }
@@
         if (postResponse.IsSuccessStatusCode)
         {
             logger.LogInformation("Client {ClientId} created successfully.", clientId);
+            return true;
         }
-        else
-        {
-            logger.LogError("Failed to create client {ClientId}. Status: {Status}", clientId, postResponse.StatusCode);
-        }
+        logger.LogError("Failed to create client {ClientId}. Status: {Status}", clientId, postResponse.StatusCode);
+        return false;
     }

Also applies to: 132-179

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

In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs` around
lines 89 - 94, Ensure failures in client creation are propagated instead of
swallowed: update EnsureClientExistsAsync (and any related helper in the same
class) so that POST errors produce a thrown exception or a non-success return
value with error details, and then change BootstrapKeycloakAsync to check the
result of calls to EnsureClientExistsAsync (for "admin-portal" and
"customer-web" and the other client calls in the 132-179 block) and if any call
indicates failure, log an error and return false (or rethrow) instead of logging
success and returning true; reference EnsureClientExistsAsync and
BootstrapKeycloakAsync and ensure the logger call at the end only runs when all
client creations succeeded.
🤖 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/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs`:
- Around line 105-107: KeycloakBootstrapService currently reads admin
credentials directly from IConfiguration (adminPass =
configuration["Keycloak:AdminPassword"]) but those values are only provisioned
to the Keycloak container, so this throws; change the service to accept the
resolved credentials via options or a settings POCO instead of reading
IConfiguration directly—inject IOptions<KeycloakOptions> (or whatever
options/type supplies AdminUsername/AdminPassword), use the injected
KeycloakOptions.AdminUsername and KeycloakOptions.AdminPassword in the
KeycloakBootstrapService constructor/methods (replace uses of
configuration["Keycloak:AdminPassword"] and
configuration["Keycloak:AdminUsername"]) so the service receives the same
credentials the Keycloak provisioning code supplies, or alternatively ensure
builder.Configuration is populated from the same source that provisions Keycloak
before this service runs.
- Line 91: The bootstrap is creating a client named "customer-web" while your
realm defines "customer-app", causing duplicate clients; update the
EnsureClientExistsAsync call in KeycloakBootstrapService (replace the
"customer-web" literal passed to EnsureClientExistsAsync) to use "customer-app"
so it matches the realm file
(infrastructure/keycloak/realms/meajudaai-realm.dev.json) and the frontend auth
configuration referenced in login-form.tsx; alternatively, if you prefer the
"customer-web" name, rename the client in the realm JSON to "customer-web" and
ensure the frontend config matches—make the names consistent across
EnsureClientExistsAsync, the realm JSON, and the frontend.
- Around line 159-160: The code is setting
ClientRepresentation.validRedirectUris which Keycloak ignores; update the
ClientRepresentation construction in KeycloakBootstrapService (where
validRedirectUris is used) to use redirectUris instead and assign the same array
value (e.g., new[] { $"{baseUrl}/*" }) so the client has allowed redirect URIs
recognized by the Keycloak Admin REST API.

---

Duplicate comments:
In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs`:
- Around line 89-94: Ensure failures in client creation are propagated instead
of swallowed: update EnsureClientExistsAsync (and any related helper in the same
class) so that POST errors produce a thrown exception or a non-success return
value with error details, and then change BootstrapKeycloakAsync to check the
result of calls to EnsureClientExistsAsync (for "admin-portal" and
"customer-web" and the other client calls in the 132-179 block) and if any call
indicates failure, log an error and return false (or rethrow) instead of logging
success and returning true; reference EnsureClientExistsAsync and
BootstrapKeycloakAsync and ensure the logger call at the end only runs when all
client creations succeeded.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f26ef565-44b2-4797-98d2-4412673f5a5f

📥 Commits

Reviewing files that changed from the base of the PR and between c87e9a6 and 8354c6e.

📒 Files selected for processing (4)
  • src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs
  • src/Shared/Database/BaseDesignTimeDbContextFactory.cs
  • src/Shared/Database/DatabaseExtensions.cs
  • src/Shared/Database/SchemaPermissionsManager.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Shared/Database/SchemaPermissionsManager.cs

Inject Keycloak settings via Options instead of direct lookups. Update customer client name constraint. Use valid JSON mapping redirectUris. Propagate early abortion states upon EnsureClientExistsAsync failures.
Copy link
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.

♻️ Duplicate comments (3)
src/Aspire/MeAjudaAi.AppHost/Program.cs (1)

15-18: ⚠️ Potential issue | 🟡 Minor

Translate the new inline comment to Portuguese.

The code is fine, but the added comment at Line 15 breaks the repo’s C# language policy for comments/XML docs.

Based on learnings: In all C# files, keep logging messages in English, but ensure comments and XML documentation (///

, /// , etc.) are written in Portuguese across the codebase.

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

In `@src/Aspire/MeAjudaAi.AppHost/Program.cs` around lines 15 - 18, Translate the
inline comment "// Register the Keycloak Bootstrap background service" into
Portuguese and replace it in Program.cs so it complies with the repo policy for
comments; update the comment immediately above the
EnvironmentHelpers.IsDevelopment(builder) check that precedes the call to
builder.Services.AddHostedService<KeycloakBootstrapService>() to a Portuguese
phrase (e.g., "// Registra o serviço em segundo plano Keycloak Bootstrap"),
keeping the surrounding code unchanged.
src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs (2)

86-94: ⚠️ Potential issue | 🟠 Major

Use the same source of truth for frontend base URLs.

AdminPortalUrl/CustomerWebUrl are not populated anywhere in Program.cs, so this still falls back to hardcoded localhost roots. That is especially brittle for admin-portal: Program.cs Line 198 only uses WithExternalHttpEndpoints(), so its actual URL can drift from http://localhost:5030, and Keycloak will store the wrong redirect URI. Push both frontend URLs through the same registration/options that creates the apps, then consume those resolved values here.

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

In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs` around
lines 86 - 94, The Keycloak bootstrap is reading AdminPortalUrl/CustomerWebUrl
from configuration (used in KeycloakBootstrapService and
EnsureClientExistsAsync) but those keys are never set and fall back to hardcoded
localhost values; update the wiring so the resolved frontend base URLs produced
by your app registration (the same source that calls WithExternalHttpEndpoints()
in Program.cs) are passed into KeycloakBootstrapService instead of relying on
raw configuration. Concretely, expose the resolved admin and customer URLs from
the registration/options that create the apps, inject or pass those values into
the KeycloakBootstrapService constructor or its Run/Boot method, and replace the
configuration["AdminPortalUrl"] / configuration["CustomerWebUrl"] lookups with
the injected/resolved values used by Program.cs so
EnsureClientExistsAsync("admin-portal", ...) and
EnsureClientExistsAsync("customer-app", ...) receive the canonical URLs.

99-102: ⚠️ Potential issue | 🟡 Minor

Don’t log normal shutdown cancellation as a transient bootstrap failure.

A clean AppHost stop cancels the same token passed into SendAsync(...), which can surface here as OperationCanceledException/TaskCanceledException. The current catch turns that into a warning even though nothing is wrong.

Minimal change
-        catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException)
+        catch (OperationCanceledException) when (ct.IsCancellationRequested)
+        {
+            throw;
+        }
+        catch (HttpRequestException ex)
+        {
+            logger.LogWarning(ex, "Transient error connecting to Keycloak during bootstrap.");
+            return false;
+        }
+        catch (TaskCanceledException ex)
         {
             logger.LogWarning(ex, "Transient error connecting to Keycloak during bootstrap.");
             return false;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs` around
lines 99 - 102, The catch in KeycloakBootstrapService (around the SendAsync
call) treats TaskCanceledException/OperationCanceledException from a normal
AppHost shutdown as a transient bootstrap failure; update the handler so that
when the CancellationToken passed to SendAsync indicates cancellation you do not
log a warning (instead rethrow or return/exit silently), and only log a warning
for genuine transient network errors (HttpRequestException or
TaskCanceledException where token.IsCancellationRequested is false); modify the
catch (Exception ex) when (ex is HttpRequestException || ex is
TaskCanceledException) logic to check the
CancellationToken.IsCancellationRequested (or specifically treat
OperationCanceledException separately) before calling logger.LogWarning in
KeycloakBootstrapService.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/Aspire/MeAjudaAi.AppHost/Program.cs`:
- Around line 15-18: Translate the inline comment "// Register the Keycloak
Bootstrap background service" into Portuguese and replace it in Program.cs so it
complies with the repo policy for comments; update the comment immediately above
the EnvironmentHelpers.IsDevelopment(builder) check that precedes the call to
builder.Services.AddHostedService<KeycloakBootstrapService>() to a Portuguese
phrase (e.g., "// Registra o serviço em segundo plano Keycloak Bootstrap"),
keeping the surrounding code unchanged.

In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs`:
- Around line 86-94: The Keycloak bootstrap is reading
AdminPortalUrl/CustomerWebUrl from configuration (used in
KeycloakBootstrapService and EnsureClientExistsAsync) but those keys are never
set and fall back to hardcoded localhost values; update the wiring so the
resolved frontend base URLs produced by your app registration (the same source
that calls WithExternalHttpEndpoints() in Program.cs) are passed into
KeycloakBootstrapService instead of relying on raw configuration. Concretely,
expose the resolved admin and customer URLs from the registration/options that
create the apps, inject or pass those values into the KeycloakBootstrapService
constructor or its Run/Boot method, and replace the
configuration["AdminPortalUrl"] / configuration["CustomerWebUrl"] lookups with
the injected/resolved values used by Program.cs so
EnsureClientExistsAsync("admin-portal", ...) and
EnsureClientExistsAsync("customer-app", ...) receive the canonical URLs.
- Around line 99-102: The catch in KeycloakBootstrapService (around the
SendAsync call) treats TaskCanceledException/OperationCanceledException from a
normal AppHost shutdown as a transient bootstrap failure; update the handler so
that when the CancellationToken passed to SendAsync indicates cancellation you
do not log a warning (instead rethrow or return/exit silently), and only log a
warning for genuine transient network errors (HttpRequestException or
TaskCanceledException where token.IsCancellationRequested is false); modify the
catch (Exception ex) when (ex is HttpRequestException || ex is
TaskCanceledException) logic to check the
CancellationToken.IsCancellationRequested (or specifically treat
OperationCanceledException separately) before calling logger.LogWarning in
KeycloakBootstrapService.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2fe498a0-2f3c-4106-9656-150cfb25a68b

📥 Commits

Reviewing files that changed from the base of the PR and between 8354c6e and d58a4bd.

📒 Files selected for processing (2)
  • src/Aspire/MeAjudaAi.AppHost/Program.cs
  • src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs

… handling

- Inject resolved endpoint references into KeycloakBootstrapService via Options.

- Avoid warning logs on normal TaskCanceledException during shutdown.

- Localize Program.cs Keycloak registration comments to Portuguese.

- Correct client name mapping to 'customer-app'.

- Refine JSON payload to use redirectUris.
Copy link
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.

🧹 Nitpick comments (1)
src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs (1)

16-21: Remove unused IConfiguration parameter.

The configuration parameter on line 19 is injected but never used anywhere in the class. The admin credentials are correctly read from keycloakOptions instead.

♻️ Suggested cleanup
 public class KeycloakBootstrapService(
     ResourceNotificationService resourceNotificationService,
     ILogger<KeycloakBootstrapService> logger,
-    IConfiguration configuration,
     Microsoft.Extensions.Options.IOptions<Options.MeAjudaAiKeycloakOptions> keycloakOptions) : BackgroundService

Also remove the unused import:

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

In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs` around
lines 16 - 21, The constructor for KeycloakBootstrapService currently injects an
unused IConfiguration parameter; remove the IConfiguration parameter from the
class constructor signature and any related using/import for IConfiguration,
keeping the other injected dependencies (ResourceNotificationService,
ILogger<KeycloakBootstrapService>,
Microsoft.Extensions.Options.IOptions<Options.MeAjudaAiKeycloakOptions>
keycloakOptions) intact, and ensure all references in the class (e.g., where
admin credentials come from keycloakOptions) continue to compile without the
removed parameter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs`:
- Around line 16-21: The constructor for KeycloakBootstrapService currently
injects an unused IConfiguration parameter; remove the IConfiguration parameter
from the class constructor signature and any related using/import for
IConfiguration, keeping the other injected dependencies
(ResourceNotificationService, ILogger<KeycloakBootstrapService>,
Microsoft.Extensions.Options.IOptions<Options.MeAjudaAiKeycloakOptions>
keycloakOptions) intact, and ensure all references in the class (e.g., where
admin credentials come from keycloakOptions) continue to compile without the
removed parameter.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 382190a0-898a-4442-bad6-290d41f63b01

📥 Commits

Reviewing files that changed from the base of the PR and between d58a4bd and f24301c.

📒 Files selected for processing (3)
  • src/Aspire/MeAjudaAi.AppHost/Options/MeAjudaAiKeycloakOptions.cs
  • src/Aspire/MeAjudaAi.AppHost/Program.cs
  • src/Aspire/MeAjudaAi.AppHost/Services/KeycloakBootstrapService.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Aspire/MeAjudaAi.AppHost/Program.cs

…rvice

Cleaner constructor signature by removing redundant configuration injection now that settings are provided via IOptions.
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

📊 Code Coverage Report

Coverage: 90.90% (extracted from Cobertura (Aggregated Direct))

📈 Coverage Details

  • Coverage badges: Coverage
  • Minimum threshold: 80% (warning) / 90% (good)
  • Report format: Auto-detected from OpenCover/Cobertura XML files
  • Coverage source: Cobertura (Aggregated Direct)

📋 Coverage Analysis

  • Line Coverage: Shows percentage of code lines executed during tests
  • Branch Coverage: Shows percentage of code branches/conditions tested
  • Complexity: Code complexity metrics for maintainability

🎯 Quality Gates

  • Pass: Coverage ≥ 90%
  • ⚠️ Warning: Coverage 80-89%
  • Fail: Coverage < 80%

📁 Artifacts

  • Coverage reports: Available in workflow artifacts
  • Test results: TRX files with detailed test execution data

This comment is updated automatically on each push to track coverage trends.

@frigini frigini merged commit fb52eb7 into master Mar 10, 2026
11 checks passed
@frigini frigini deleted the feature/sprint-8b2-core-tech branch March 10, 2026 00:28
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