Skip to content

Sprint 8c: add provider web app & slug implementation#165

Open
frigini wants to merge 1 commit intomasterfrom
feature/sprint-8c-provider-web
Open

Sprint 8c: add provider web app & slug implementation#165
frigini wants to merge 1 commit intomasterfrom
feature/sprint-8c-provider-web

Conversation

@frigini
Copy link
Owner

@frigini frigini commented Mar 13, 2026

Summary by CodeRabbit

Novas Funcionalidades

  • Provedores agora possuem identificadores únicos amigáveis para URLs (slugs) gerados automaticamente a partir do nome.
  • Funcionalidade de busca expandida para incluir suporte a slugs de provedores.

…ities, DTOs, mappers, repositories, event handlers, APIs, and comprehensive tests.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

Adiciona suporte a Slug como identificador amigável para URLs em provedores, com geração automática a partir do nome, persistência no banco de dados, e exposição através de DTOs, repositórios e eventos de domínio. Inclui utilidade SlugHelper para normalização de texto em slugs.

Changes

Cohort / File(s) Summary
SlugHelper Utility
src/Shared/Utilities/SlugHelper.cs, tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs
Novo utilitário SlugHelper com método Generate para criar slugs URL-amigáveis a partir de texto, incluindo normalização de diacríticos, remoção de caracteres especiais e compressão de hífens. Testes abrangentes validam casos com caracteres acentuados, espaços e entrada vazia.
Provider Domain & Events
src/Modules/Providers/Domain/Entities/Provider.cs, src/Modules/Providers/Domain/Events/ProviderRegisteredDomainEvent.cs, src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs
Adiciona propriedade Slug ao Provider, gerada automaticamente de Name usando SlugHelper. Slug é atualizado quando Name muda via UpdateProfile. Eventos de domínio expandidos para incluir Slug nos payloads.
Provider Repository Interface & Implementation
src/Modules/Providers/Domain/Repositories/IProviderRepository.cs, src/Modules/Providers/Infrastructure/Persistence/Repositories/ProviderRepository.cs
Novo método GetBySlugAsync para consultar Provider por slug. Implementação filtra provedores não deletados e retorna primeiro match de slug.
Provider Database Configuration
src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs
Mapeia propriedade Slug com comprimento máximo 120, requerido, e adiciona índice único "ix_providers_slug" para otimizar buscas por slug.
Provider DTOs (Contracts Layer)
src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderBasicDto.cs, src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderDto.cs, src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderIndexingDto.cs
Todos os DTOs de contrato expandem assinatura para incluir nova propriedade Slug do tipo string.
Provider DTOs (Application Layer)
src/Modules/Providers/Application/DTOs/ProviderDto.cs, src/Modules/Providers/Application/DTOs/PublicProviderDto.cs
Adiciona propriedade Slug aos DTOs de aplicação, atualiza assinaturas de construtor para acomodar novo campo.
Provider Mappers & Handlers
src/Modules/Providers/Application/Mappers/ProviderMapper.cs, src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdQueryHandler.cs, src/Modules/Providers/Application/ModuleApi/ProvidersModuleApi.cs
Mappers e handlers expandidos para incluir Slug ao transformar Provider para DTOs públicos e internos. ModuleApi passa Slug através de ModuleProviderIndexingDto, ModuleProviderDto e ModuleProviderBasicDto.
Provider Tests (Unit)
src/Modules/Providers/Tests/Unit/API/Endpoints/*, src/Modules/Providers/Tests/Unit/Application/Services/ProvidersModuleApiTests.cs, src/Modules/Providers/Tests/Unit/Domain/Entities/ProviderTests.cs, src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/*
Atualiza testes para incluir Slug em construções de ProviderDto e eventos de domínio. Testes de entidade Provider validam geração automática de slug a partir de nome e sincronização em eventos.
SearchableProvider Domain
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
Adiciona propriedade Slug com validação em Create, Reconstitute e UpdateBasicInfo. Slug é normalizado (minúsculas, espaços trimmed).
SearchableProvider Database Configuration
src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs
Mapeia Slug como requerido com comprimento máximo 200 caracteres, mapeado para coluna "slug".
SearchableProvider DTOs & Repositories
src/Modules/SearchProviders/Application/DTOs/SearchableProviderDto.cs, src/Modules/SearchProviders/Infrastructure/Persistence/DTOs/ProviderSearchResultDto.cs, src/Modules/SearchProviders/Infrastructure/Persistence/Repositories/SearchableProviderRepository.cs
DTOs expandem assinatura para incluir Slug. Repositório projeta Slug no SELECT SQL e passa para método Reconstitute na mapeamento de domínio.
SearchableProvider Application Layer
src/Modules/SearchProviders/Application/Handlers/SearchProvidersQueryHandler.cs, src/Modules/SearchProviders/Application/ModuleApi/SearchProvidersModuleApi.cs
Handler e ModuleApi atualizam para incluir Slug: p.Slug na projeção LINQ e ao chamar Create/UpdateBasicInfo de SearchableProvider.
SearchableProvider Tests (Unit & Integration)
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs, src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs, src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs, src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs, src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs, src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs
Testes atualizados para passar slug ao criar SearchableProvider, com geração de slug a partir de nome em helpers. Assinaturas de Create e UpdateBasicInfo expandidas com slug como parâmetro.
UI & Admin Tests
prompts/design-react-project.md, tests/MeAjudaAi.Web.Admin.Tests/Pages/ProvidersPageTests.cs
Documentação de design React adicionada. Teste de página admin atualizado com ModuleProviderDto expandido (11 parâmetros em vez de 6).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • melhorias nos módulos users e providers #6: Modifica eventos de domínio ProviderRegisteredDomainEvent e ProviderProfileUpdatedDomainEvent com campos adicionais na entidade Provider, diretamente afetado pelas mudanças de Slug neste PR.
  • Service provider module implementation #5: Introduz mudanças extensas aos DTOs, eventos e repositório de Providers, com sobreposição significativa de arquivos afetados pelo suporte a Slug deste PR.
  • Feature/sprint 8b auth onboarding #136: Modifica domínio de Providers e DTOs relacionados com campos adicionais, afetando os mesmos arquivos base de provider que recebem Slug neste PR.

Poem

🐰 Pela trilha de Slugs eu corro,
URLs amigáveis vão brilhar,
De Provider até SearchableProvider,
Normalizado e pronto pra indexar! 🌿✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning Nenhuma descrição foi fornecida pelo autor, violando completamente o template de PR obrigatório. Adicione uma descrição completa seguindo o template: Resumo, Problema, Solução, Mudanças, Impacto, Testes e Checklist.
Docstring Coverage ⚠️ Warning Docstring coverage is 30.16% 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 O título aborda a implementação de slug e web app do provider, alinhado com as mudanças principais do changeset.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/sprint-8c-provider-web
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 4

Caution

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

⚠️ Outside diff range comments (1)
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (1)

147-170: ⚠️ Potential issue | 🟡 Minor

Normalize o Slug também na reconstituição.

Reconstitute ainda copia o valor bruto do banco, então dados legados com espaços ou casing diferente quebram a mesma invariável garantida por Create e UpdateBasicInfo.

🛠️ Ajuste sugerido
     internal static SearchableProvider Reconstitute(
         Guid id,
         Guid providerId,
         string name,
         string slug,
         GeoPoint location,
         ESubscriptionTier subscriptionTier,
         decimal averageRating,
         int totalReviews,
         Guid[] serviceIds,
         bool isActive,
         string? description = null,
         string? city = null,
         string? state = null)
     {
+        if (string.IsNullOrWhiteSpace(slug))
+        {
+            throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));
+        }
+
         var searchableProvider = new SearchableProvider(
             new SearchableProviderId(id),
             providerId,
             name,
             location,
             subscriptionTier)
         {
-            Slug = slug,
+            Slug = slug.Trim().ToLowerInvariant(),
             Description = description,
             City = city,
             State = state,
             AverageRating = averageRating,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 147 - 170, No método Reconstitute da classe SearchableProvider, normalize
o valor de Slug antes de atribuí-lo para manter a mesma invariante aplicada em
Create e UpdateBasicInfo; localize SearchableProvider.Reconstitute e substituir
a atribuição Slug = slug por uma atribuição que aplica a mesma rotina de
normalização usada por Create/UpdateBasicInfo (por exemplo o helper/método de
slugificação existente — nome similar a NormalizeSlug/Slugify — ou reimplemente
a mesma lógica de trim, lowercase e remoção de espaços/caracteres), garantindo
que dados legados sejam normalizados ao reconstituir a entidade.
🧹 Nitpick comments (14)
src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs (1)

188-197: Prefira SlugHelper.Generate no teste para evitar divergência de regra.

Hoje o teste duplica a lógica de slug manualmente; isso pode mascarar regressões quando a regra oficial mudar.

Refactor sugerido
+using MeAjudaAi.Shared.Utilities;
...
 var providerName = _faker.Company.CompanyName();
 var provider = SearchableProvider.Create(
     providerId: providerId,
     name: providerName,
-    slug: providerName.ToLower().Replace(" ", "-"),
+    slug: SlugHelper.Generate(providerName),
     location: location,
     subscriptionTier: _faker.Random.Enum<ESubscriptionTier>(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs`
around lines 188 - 197, O teste está replicando a lógica de geração de slug
manualmente; em vez de usar providerName.ToLower().Replace(" ", "-") passe o
slug gerado por SlugHelper.Generate(providerName) ao criar o objeto via
SearchableProvider.Create (referenciar providerName e o parâmetro slug) para
garantir que o teste acompanhe a regra oficial de geração de slugs.
prompts/design-react-project.md (1)

138-152: Adicione especificador de linguagem ao bloco de código.

O bloco de código para variáveis CSS não possui especificador de linguagem, o que dispara um aviso do markdownlint. Adicione css ou text para melhorar a formatação e silenciar o warning.

📝 Sugestão de correção
-```
+```css
 bg-surface, bg-surface-raised       → fundos
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prompts/design-react-project.md` around lines 138 - 152, The code block
containing the CSS variable mappings (lines with identifiers like bg-surface,
bg-primary, bg-destructive, text-foreground, border-border, ring-ring, etc.)
lacks a language specifier; update the fenced code block start to include a
language (e.g., ```css or ```text) so markdownlint warning is silenced and
formatting is improved—locate the block showing the mappings and change the
opening fence to ```css.
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs (1)

6-7: Considere adicionar o atributo de categoria para consistência.

Os outros arquivos de teste no projeto utilizam [Trait("Category", "Unit")] para categorização. Adicionar este atributo manteria a consistência e facilitaria a filtragem de testes por categoria.

♻️ Sugestão de refatoração
+[Trait("Category", "Unit")]
 public class SlugHelperTests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs` around lines
6 - 7, A classe de teste SlugHelperTests está faltando a categorização
consistente usada no projeto; adicione o atributo [Trait("Category", "Unit")]
acima da declaração da classe SlugHelperTests (ou em cada método de teste se
preferir granularidade) para alinhar com os outros testes; verifique que o
namespace/xUnit esteja disponível (usando Xunit) e mantenha o mesmo padrão de
categoria usado pelo restante do repositório.
src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs (1)

44-47: Padronize o limite de tamanho do Slug entre Providers e SearchProviders.

Aqui está 200, mas no módulo Providers está 120. Vale alinhar (ou extrair constante compartilhada) para evitar drift de schema/regra de domínio.

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
`@src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs`
around lines 44 - 47, In SearchableProviderConfiguration (builder.Property(p =>
p.Slug)) the Slug max length is set to 200 but Providers uses 120; change
HasMaxLength(200) to the shared canonical value (preferably use the constant
from Shared.Contracts, e.g. import and reference the Slug length constant) or
set it to 120 to match Providers, and update any related EF migration/schema to
keep database and domain rules aligned.
src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs (1)

70-71: Boa atualização dos testes para o novo construtor, mas falta validar o payload do Slug.

Como este PR introduz Slug no evento de domínio, vale reforçar o Verify para checar o conteúdo publicado (não só o tipo), assim que o contrato de integração expuser esse campo.

Also applies to: 96-97, 123-124

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

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs`
around lines 70 - 71, Atualize os asserts/verificações nos testes em
ProviderRegisteredDomainEventHandlerTests para não só checar o tipo do evento
publicado, mas também validar o payload.Slug; especificamente, ao verificar a
chamada de Publish/Send (o Verify usando o mock do bus ou publisher), confirme
que o evento publicado (ProviderRegisteredDomainEvent) tem a propriedade Slug
igual a "provider-test". Aplique a mesma verificação nas outras ocorrências
apontadas (asserções nas seções correspondentes às linhas 96-97 e 123-124) para
garantir que todos os testes validem o conteúdo do Slug além do tipo do evento.
src/Modules/Providers/Application/Mappers/ProviderMapper.cs (1)

17-36: Mistura inconsistente de parâmetros nomeados e posicionais.

O método começa com parâmetros nomeados (linhas 18-22: Id, UserId, Name, Slug, Type) mas depois muda para parâmetros posicionais (linhas 23-35). Isso reduz a legibilidade e aumenta o risco de erros se a ordem dos parâmetros do construtor mudar.

♻️ Usar parâmetros nomeados consistentemente
     public static ProviderDto ToDto(this Provider provider)
     {
         return new ProviderDto(
             Id: provider.Id.Value,
             UserId: provider.UserId,
             Name: provider.Name,
             Slug: provider.Slug,
             Type: provider.Type,
-            provider.BusinessProfile.ToDto(),
-            provider.Status,
-            provider.VerificationStatus,
-            provider.Tier,
-            provider.Documents.Select(d => d.ToDto()).ToList(),
-            provider.Qualifications.Select(q => q.ToDto()).ToList(),
-            provider.Services.Select(s => s.ToDto()).ToList(),
-            provider.CreatedAt,
-            provider.UpdatedAt,
-            provider.IsDeleted,
-            provider.DeletedAt,
-            provider.SuspensionReason,
-            provider.RejectionReason
+            BusinessProfile: provider.BusinessProfile.ToDto(),
+            Status: provider.Status,
+            VerificationStatus: provider.VerificationStatus,
+            Tier: provider.Tier,
+            Documents: provider.Documents.Select(d => d.ToDto()).ToList(),
+            Qualifications: provider.Qualifications.Select(q => q.ToDto()).ToList(),
+            Services: provider.Services.Select(s => s.ToDto()).ToList(),
+            CreatedAt: provider.CreatedAt,
+            UpdatedAt: provider.UpdatedAt,
+            IsDeleted: provider.IsDeleted,
+            DeletedAt: provider.DeletedAt,
+            SuspensionReason: provider.SuspensionReason,
+            RejectionReason: provider.RejectionReason
         );
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Providers/Application/Mappers/ProviderMapper.cs` around lines 17
- 36, O trecho que instancia ProviderDto mistura argumentos nomeados e
posicionais; abraçar consistência e segurança: atualize a chamada em
ProviderMapper para usar apenas parâmetros nomeados (por exemplo Id:, UserId:,
Name:, Slug:, Type:, BusinessProfile:, Status:, VerificationStatus:, Tier:,
Documents:, Qualifications:, Services:, CreatedAt:, UpdatedAt:, IsDeleted:,
DeletedAt:, SuspensionReason:, RejectionReason:) referenciando as propriedades
atuais (p.ex. provider.BusinessProfile.ToDto(),
provider.Documents.Select(...).ToList(), etc.), garantindo que cada valor
corresponda explicitamente ao parâmetro do construtor ProviderDto.
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs (2)

164-172: Derivação de slug inconsistente com a produção.

Assim como no arquivo de testes unitários do repositório, a geração de slug aqui usa name.ToLower().Replace(" ", "-") (linha 167), que não corresponde ao comportamento de SlugHelper.Generate(). Em testes de integração isso é ainda mais crítico, pois os dados persistidos no banco podem ter slugs que nunca seriam gerados em produção.

♻️ Usar SlugHelper.Generate para consistência
         var provider = SearchableProvider.Create(
             providerId: providerId,
             name: name,
-            slug: name.ToLower().Replace(" ", "-"),
+            slug: SlugHelper.Generate(name),
             location: location,
             subscriptionTier: tier,
             description: description,
             city: city,
             state: state);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
around lines 164 - 172, A geração de slug em SearchableProvider.Create está
usando name.ToLower().Replace(" ", "-"), o que difere do comportamento de
produção; substitua essa expressão pelo uso de SlugHelper.Generate(name) ao
construir o argumento slug (na chamada de SearchableProvider.Create) e
importe/ajuste o namespace se necessário para garantir que os slugs em testes de
integração sejam gerados exatamente como em produção.

192-200: Mesma inconsistência de slug no segundo helper.

O método CreateTestSearchableProviderWithProviderId também usa a derivação naive de slug na linha 195.

♻️ Usar SlugHelper.Generate para consistência
         var provider = SearchableProvider.Create(
             providerId: providerId,
             name: name,
-            slug: name.ToLower().Replace(" ", "-"),
+            slug: SlugHelper.Generate(name),
             location: location,
             subscriptionTier: tier,
             description: description,
             city: city,
             state: state);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
around lines 192 - 200, The CreateTestSearchableProviderWithProviderId helper
currently derives the slug naively using name.ToLower().Replace(" ", "-");
update this to use the centralized SlugHelper.Generate method for consistency —
modify the provider creation in CreateTestSearchableProviderWithProviderId (the
SearchableProvider.Create(...) call that sets slug) to call
SlugHelper.Generate(name) instead of the manual replace so slugs match other
helpers.
src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs (1)

53-67: Derivação de slug inconsistente com a produção.

A geração de slug na linha 55 usa actualName.ToLower().Replace(" ", "-"), que difere significativamente do SlugHelper.Generate() usado em produção. O helper de produção também:

  1. Remove diacríticos (ex: "café" → "cafe")
  2. Remove caracteres não-alfanuméricos (ex: "&" é removido)
  3. Colapsa hífens duplicados
  4. Faz trim de hífens nas extremidades

Isso pode criar dados de teste que nunca existiriam em produção, potencialmente mascarando bugs ou causando falsos positivos.

♻️ Usar SlugHelper.Generate para consistência
-        var actualSlug = actualName.ToLower().Replace(" ", "-");
+        var actualSlug = SlugHelper.Generate(actualName);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs`
around lines 53 - 67, The test builds a slug with actualName.ToLower().Replace("
", "-") which diverges from production slug behavior; update the test to
generate slugs using the same production helper (call
SlugHelper.Generate(actualName) when constructing the SearchableProvider in
SearchableProvider.Create) so diacritics, non-alphanumerics, duplicate hyphens
and edge hyphens are handled consistently with production (ensure any required
using/import for SlugHelper is added).
src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs (1)

43-46: Documentação XML deve estar em Português.

As tags <summary> estão em inglês, mas de acordo com a política de linguagem do projeto, comentários e documentação XML devem estar em Português.

📝 Traduzir documentação XML para Português
-    /// <summary>
-    /// Verifies that HandleAsync publishes a ProviderProfileUpdatedIntegrationEvent with correct provider details
-    /// when processing a valid profile update domain event.
-    /// </summary>
+    /// <summary>
+    /// Verifica que HandleAsync publica um ProviderProfileUpdatedIntegrationEvent com os detalhes corretos do provedor
+    /// ao processar um evento de domínio de atualização de perfil válido.
+    /// </summary>
-    /// <summary>
-    /// Verifies that when message bus publishing fails, the handler logs an error containing
-    /// "Error handling ProviderProfileUpdatedDomainEvent" and re-throws the exception.
-    /// </summary>
+    /// <summary>
+    /// Verifica que quando a publicação no message bus falha, o handler registra um erro contendo
+    /// "Error handling ProviderProfileUpdatedDomainEvent" e relança a exceção.
+    /// </summary>

Baseado em learnings: "Comments and XML documentation can remain in Portuguese, logs should be in English".

Also applies to: 82-85

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

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs`
around lines 43 - 46, A documentação XML nas tags <summary> para o teste que
descreve a verificação de HandleAsync deve ser traduzida para Português;
localize no arquivo ProviderProfileUpdatedDomainEventHandlerTests (as
ocorrências atuais nas linhas próximas aos trechos que descrevem "Verifies that
HandleAsync publishes a ProviderProfileUpdatedIntegrationEvent..." e a segunda
ocorrência perto das linhas 82-85) e substitua o texto em inglês por um resumo
em Português mantendo as mesmas tags XML /// <summary> ... </summary>, por
exemplo descrevendo que o teste verifica se HandleAsync publica um
ProviderProfileUpdatedIntegrationEvent com os detalhes corretos do provider;
preserve a formatação XML e outros comentários adjacentes.
src/Shared/Utilities/SlugHelper.cs (1)

26-48: Implementação sólida com uma consideração de edge case.

A implementação está bem estruturada usando GeneratedRegex para performance. Um edge case a considerar: se o texto de entrada contiver apenas caracteres especiais (ex: "!@#$%"), o método retornará uma string vazia, o que causará ArgumentException em SearchableProvider.Create() e UpdateBasicInfo().

Isso é comportamento defensivo adequado, mas pode ser útil documentar ou validar isso no ponto de entrada (ex: validação de nome do provider antes de gerar o slug).

📝 Sugestão: Adicionar documentação sobre o edge case
     /// <summary>
     /// Gera um slug a partir de um texto
     /// </summary>
     /// <param name="text">Texto original</param>
-    /// <returns>Slug formatado</returns>
+    /// <returns>Slug formatado ou string vazia se o texto não contiver caracteres alfanuméricos válidos</returns>
+    /// <remarks>
+    /// Se o texto contiver apenas caracteres especiais, o método retornará string vazia.
+    /// Chamadores devem validar o resultado antes de usar em contextos que requerem slug não-vazio.
+    /// </remarks>
     public static string Generate(string text)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Utilities/SlugHelper.cs` around lines 26 - 48, The Generate method
can return an empty string for inputs with only non-alphanumeric chars (e.g.
"!@#$%"), which later causes an ArgumentException in SearchableProvider.Create
and UpdateBasicInfo; add defensive validation at the call sites: in
SearchableProvider.Create and UpdateBasicInfo, validate the provider name (not
null/whitespace) before calling SlugHelper.Generate, then call Generate and if
it returns null/empty throw a clear ArgumentException (or return a meaningful
default/validation error) that explains the name produced no valid slug;
alternatively, if you prefer centralizing the check, modify SlugHelper.Generate
to detect the empty-result case and either throw a descriptive ArgumentException
or return a documented fallback value, and update callers to handle that
behavior accordingly.
src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs (1)

357-362: Prefira gerar o Slug com a mesma utility da produção.

Os fixtures aqui misturam slug hardcoded e ToLower().Replace(" ", "-"), o que pode divergir do SlugHelper em casos de acento, pontuação ou múltiplos espaços. Vale centralizar isso para os testes continuarem refletindo a regra real.

♻️ Exemplo
+using MeAjudaAi.Shared.Utilities;
...
             var name = $"Provider {i + 1}";
+            var slug = SlugHelper.Generate(name);
             providers.Add(SearchableProvider.Create(
                 providerId: Guid.NewGuid(),
                 name: name,
-                slug: name.ToLower().Replace(" ", "-"),
+                slug: slug,
                 location: new GeoPoint(-23.5505 + i * 0.01, -46.6333 + i * 0.01),

Also applies to: 396-405

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

In
`@src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs`
around lines 357 - 362, Os fixtures criam slugs manualmente, o que pode divergir
da lógica de produção; update os testes para gerar o slug usando a mesma utility
que a aplicação usa (ex.: SlugHelper or whatever slug generator is used) ao
construir o SearchableProvider via SearchableProvider.Create so that
providerId/name/slug use the canonical slugging logic; apply the same change to
the other fixture block referenced (around the 396-405 area) so tests mirror
production behavior for accents, punctuation and multiple spaces.
src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs (1)

26-34: Faltam cenários para a nova invariável de Slug.

A suíte foi adaptada para passar slug, mas ainda não cobre os novos guards de Create/UpdateBasicInfo nem a normalização para lowercase. Um teste com slug vazio/branco e outro verificando o valor normalizado protegeriam melhor a mudança.

Also applies to: 58-63, 73-77, 90-111

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

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`
around lines 26 - 34, Os testes faltam cenários cobrindo a nova invariável slug:
adicione casos unitários em SearchableProviderTests que verifiquem (1) que
Create(...) e UpdateBasicInfo(...) falham (lançam/retornam erro de validação)
quando slug é null/empty/whitespace e (2) que quando um slug válido com letras
maiúsculas é passado, o valor armazenado em SearchableProvider.Slug é
normalizado para lowercase; foque nas chamadas SearchableProvider.Create(...) e
no método UpdateBasicInfo(...) para inserir asserts que confirmem as guards e a
normalização.
src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs (1)

158-222: Os testes ainda não validam a propagação do Slug.

Os fixtures agora populam Slug, mas as asserções só conferem contagem/chamada ao repositório. Se o mapping da API devolver ou persistir o slug errado, esses cenários continuam verdes. Vale validar o Slug no resultado de busca e nas entidades enviadas para AddAsync/UpdateAsync.

♻️ Exemplo
         provider.ProviderId.Should().Be(providerId);
         provider.Name.Should().Be("Provider 1");
+        provider.Slug.Should().Be("provider-1");
...
-        _repositoryMock.Verify(x => x.AddAsync(It.IsAny<SearchableProvider>(), It.IsAny<CancellationToken>()), Times.Once);
+        _repositoryMock.Verify(x => x.AddAsync(
+            It.Is<SearchableProvider>(p => p.Slug == "test-provider"),
+            It.IsAny<CancellationToken>()), Times.Once);
...
-        _repositoryMock.Verify(x => x.UpdateAsync(existingProvider, It.IsAny<CancellationToken>()), Times.Once);
+        _repositoryMock.Verify(x => x.UpdateAsync(
+            It.Is<SearchableProvider>(p => p.Slug == "updated-provider"),
+            It.IsAny<CancellationToken>()), Times.Once);

Also applies to: 327-357, 375-405, 468-509

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

In
`@src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs`
around lines 158 - 222, The tests currently don't assert that the provider Slug
is propagated; update the unit tests that exercise SearchProvidersAsync (and the
other search/add/update tests mentioned) to assert provider.Slug equals the
expected "provider-1" in the returned ModuleSearchableProviderDto (from
PagedResult<SearchableProviderDto>) and add mock verifications that repository
methods (e.g., AddAsync/UpdateAsync) are called with entities whose Slug
property equals the expected slug; locate assertions around
result.Value.Items[0] (variable provider) and the mock setups on
_queryDispatcherMock and repository mocks to add these Slug checks so mapping
and persistence of Slug are validated.
🤖 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/Providers/Domain/Entities/Provider.cs`:
- Around line 139-140: O campo Slug está sendo gerado apenas a partir do nome
(Slug = SlugHelper.Generate(Name)) o que causa colisões com o índice único em
providers.slug; altere a lógica para garantir slug único antes de persistir:
crie/usar um método como EnsureUniqueSlugAsync/GenerateUniqueSlug que tenta
SlugHelper.Generate(Name) e, enquanto ProviderRepository.SlugExists(slug,
excludeId) for true, anexe um sufixo incremental ("-1", "-2", ...) ou um hash
curto até obter um slug livre, então atribua Slug ao entity; alternativamente,
ao invés de só confiar no banco, capture conflito de unique constraint durante
Save/Update e reexecute a geração/atualização do slug e re-persista; aplique
mesma correção nas outras ocorrências mencionadas (blocos em 174-189 e 211-241)
referenciando as mesmas funções e o repositório de providers.

In `@src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs`:
- Around line 12-22: The domain event ProviderProfileUpdatedDomainEvent now
includes Slug but the integration event ProviderProfileUpdatedIntegrationEvent
and the mapper ProviderEventMappers.ToIntegrationEvent() were not updated; add a
nullable Slug property to ProviderProfileUpdatedIntegrationEvent (e.g., string?
Slug = null) and update ProviderEventMappers.ToIntegrationEvent to set Slug:
domainEvent.Slug when constructing the integration event so consumers like
SearchProvidersModuleApi receive slug updates when "Slug" appears in
UpdatedFields.

In `@src/Modules/Providers/Domain/Events/ProviderRegisteredDomainEvent.cs`:
- Around line 27-28: O evento de domínio ProviderRegisteredDomainEvent agora
inclui o campo Slug, mas ele não está sendo propagado: atualize o contrato de
integração ProviderRegisteredIntegrationEvent para expor uma propriedade Slug
(mesmo tipo que no domínio) e ajuste o mapeador em ProviderEventMappers (a
função que transforma ProviderRegisteredDomainEvent em
ProviderRegisteredIntegrationEvent) para atribuir integrationEvent.Slug =
domainEvent.Slug; garanta também que construtores/serialização do
ProviderRegisteredIntegrationEvent aceitem/definam o Slug para evitar perda de
dado ao publicar.

In
`@src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs`:
- Around line 250-252: The unique index declared by ProviderConfiguration via
builder.HasIndex(p => p.Slug).IsUnique() will cause persistence errors for
providers with duplicate names; remove the uniqueness constraint (drop
.IsUnique()) or replace it with a non-unique index, or alternatively make the
index composite (e.g., include a tenant or owner key) and/or update the slug
generation logic to append a disambiguating suffix so Slug values are unique;
locate the HasIndex(p => p.Slug) call in ProviderConfiguration and apply one of
these fixes consistently with existing slug generation code.

---

Outside diff comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 147-170: No método Reconstitute da classe SearchableProvider,
normalize o valor de Slug antes de atribuí-lo para manter a mesma invariante
aplicada em Create e UpdateBasicInfo; localize SearchableProvider.Reconstitute e
substituir a atribuição Slug = slug por uma atribuição que aplica a mesma rotina
de normalização usada por Create/UpdateBasicInfo (por exemplo o helper/método de
slugificação existente — nome similar a NormalizeSlug/Slugify — ou reimplemente
a mesma lógica de trim, lowercase e remoção de espaços/caracteres), garantindo
que dados legados sejam normalizados ao reconstituir a entidade.

---

Nitpick comments:
In `@prompts/design-react-project.md`:
- Around line 138-152: The code block containing the CSS variable mappings
(lines with identifiers like bg-surface, bg-primary, bg-destructive,
text-foreground, border-border, ring-ring, etc.) lacks a language specifier;
update the fenced code block start to include a language (e.g., ```css or
```text) so markdownlint warning is silenced and formatting is improved—locate
the block showing the mappings and change the opening fence to ```css.

In `@src/Modules/Providers/Application/Mappers/ProviderMapper.cs`:
- Around line 17-36: O trecho que instancia ProviderDto mistura argumentos
nomeados e posicionais; abraçar consistência e segurança: atualize a chamada em
ProviderMapper para usar apenas parâmetros nomeados (por exemplo Id:, UserId:,
Name:, Slug:, Type:, BusinessProfile:, Status:, VerificationStatus:, Tier:,
Documents:, Qualifications:, Services:, CreatedAt:, UpdatedAt:, IsDeleted:,
DeletedAt:, SuspensionReason:, RejectionReason:) referenciando as propriedades
atuais (p.ex. provider.BusinessProfile.ToDto(),
provider.Documents.Select(...).ToList(), etc.), garantindo que cada valor
corresponda explicitamente ao parâmetro do construtor ProviderDto.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs`:
- Around line 43-46: A documentação XML nas tags <summary> para o teste que
descreve a verificação de HandleAsync deve ser traduzida para Português;
localize no arquivo ProviderProfileUpdatedDomainEventHandlerTests (as
ocorrências atuais nas linhas próximas aos trechos que descrevem "Verifies that
HandleAsync publishes a ProviderProfileUpdatedIntegrationEvent..." e a segunda
ocorrência perto das linhas 82-85) e substitua o texto em inglês por um resumo
em Português mantendo as mesmas tags XML /// <summary> ... </summary>, por
exemplo descrevendo que o teste verifica se HandleAsync publica um
ProviderProfileUpdatedIntegrationEvent com os detalhes corretos do provider;
preserve a formatação XML e outros comentários adjacentes.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs`:
- Around line 70-71: Atualize os asserts/verificações nos testes em
ProviderRegisteredDomainEventHandlerTests para não só checar o tipo do evento
publicado, mas também validar o payload.Slug; especificamente, ao verificar a
chamada de Publish/Send (o Verify usando o mock do bus ou publisher), confirme
que o evento publicado (ProviderRegisteredDomainEvent) tem a propriedade Slug
igual a "provider-test". Aplique a mesma verificação nas outras ocorrências
apontadas (asserções nas seções correspondentes às linhas 96-97 e 123-124) para
garantir que todos os testes validem o conteúdo do Slug além do tipo do evento.

In
`@src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs`:
- Around line 44-47: In SearchableProviderConfiguration (builder.Property(p =>
p.Slug)) the Slug max length is set to 200 but Providers uses 120; change
HasMaxLength(200) to the shared canonical value (preferably use the constant
from Shared.Contracts, e.g. import and reference the Slug length constant) or
set it to 120 to match Providers, and update any related EF migration/schema to
keep database and domain rules aligned.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Around line 164-172: A geração de slug em SearchableProvider.Create está
usando name.ToLower().Replace(" ", "-"), o que difere do comportamento de
produção; substitua essa expressão pelo uso de SlugHelper.Generate(name) ao
construir o argumento slug (na chamada de SearchableProvider.Create) e
importe/ajuste o namespace se necessário para garantir que os slugs em testes de
integração sejam gerados exatamente como em produção.
- Around line 192-200: The CreateTestSearchableProviderWithProviderId helper
currently derives the slug naively using name.ToLower().Replace(" ", "-");
update this to use the centralized SlugHelper.Generate method for consistency —
modify the provider creation in CreateTestSearchableProviderWithProviderId (the
SearchableProvider.Create(...) call that sets slug) to call
SlugHelper.Generate(name) instead of the manual replace so slugs match other
helpers.

In
`@src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs`:
- Around line 357-362: Os fixtures criam slugs manualmente, o que pode divergir
da lógica de produção; update os testes para gerar o slug usando a mesma utility
que a aplicação usa (ex.: SlugHelper or whatever slug generator is used) ao
construir o SearchableProvider via SearchableProvider.Create so that
providerId/name/slug use the canonical slugging logic; apply the same change to
the other fixture block referenced (around the 396-405 area) so tests mirror
production behavior for accents, punctuation and multiple spaces.

In
`@src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs`:
- Around line 158-222: The tests currently don't assert that the provider Slug
is propagated; update the unit tests that exercise SearchProvidersAsync (and the
other search/add/update tests mentioned) to assert provider.Slug equals the
expected "provider-1" in the returned ModuleSearchableProviderDto (from
PagedResult<SearchableProviderDto>) and add mock verifications that repository
methods (e.g., AddAsync/UpdateAsync) are called with entities whose Slug
property equals the expected slug; locate assertions around
result.Value.Items[0] (variable provider) and the mock setups on
_queryDispatcherMock and repository mocks to add these Slug checks so mapping
and persistence of Slug are validated.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`:
- Around line 26-34: Os testes faltam cenários cobrindo a nova invariável slug:
adicione casos unitários em SearchableProviderTests que verifiquem (1) que
Create(...) e UpdateBasicInfo(...) falham (lançam/retornam erro de validação)
quando slug é null/empty/whitespace e (2) que quando um slug válido com letras
maiúsculas é passado, o valor armazenado em SearchableProvider.Slug é
normalizado para lowercase; foque nas chamadas SearchableProvider.Create(...) e
no método UpdateBasicInfo(...) para inserir asserts que confirmem as guards e a
normalização.

In `@src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs`:
- Around line 188-197: O teste está replicando a lógica de geração de slug
manualmente; em vez de usar providerName.ToLower().Replace(" ", "-") passe o
slug gerado por SlugHelper.Generate(providerName) ao criar o objeto via
SearchableProvider.Create (referenciar providerName e o parâmetro slug) para
garantir que o teste acompanhe a regra oficial de geração de slugs.

In
`@src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs`:
- Around line 53-67: The test builds a slug with actualName.ToLower().Replace("
", "-") which diverges from production slug behavior; update the test to
generate slugs using the same production helper (call
SlugHelper.Generate(actualName) when constructing the SearchableProvider in
SearchableProvider.Create) so diacritics, non-alphanumerics, duplicate hyphens
and edge hyphens are handled consistently with production (ensure any required
using/import for SlugHelper is added).

In `@src/Shared/Utilities/SlugHelper.cs`:
- Around line 26-48: The Generate method can return an empty string for inputs
with only non-alphanumeric chars (e.g. "!@#$%"), which later causes an
ArgumentException in SearchableProvider.Create and UpdateBasicInfo; add
defensive validation at the call sites: in SearchableProvider.Create and
UpdateBasicInfo, validate the provider name (not null/whitespace) before calling
SlugHelper.Generate, then call Generate and if it returns null/empty throw a
clear ArgumentException (or return a meaningful default/validation error) that
explains the name produced no valid slug; alternatively, if you prefer
centralizing the check, modify SlugHelper.Generate to detect the empty-result
case and either throw a descriptive ArgumentException or return a documented
fallback value, and update callers to handle that behavior accordingly.

In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs`:
- Around line 6-7: A classe de teste SlugHelperTests está faltando a
categorização consistente usada no projeto; adicione o atributo
[Trait("Category", "Unit")] acima da declaração da classe SlugHelperTests (ou em
cada método de teste se preferir granularidade) para alinhar com os outros
testes; verifique que o namespace/xUnit esteja disponível (usando Xunit) e
mantenha o mesmo padrão de categoria usado pelo restante do repositório.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ca172342-9452-418d-9afe-182a55ada172

📥 Commits

Reviewing files that changed from the base of the PR and between c137cdf and 0a1f6a3.

⛔ Files ignored due to path filters (2)
  • src/Web/MeAjudaAi.Web.Admin/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Web.Admin.Tests/packages.lock.json is excluded by !**/packages.lock.json
📒 Files selected for processing (39)
  • prompts/design-react-project.md
  • src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderBasicDto.cs
  • src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderDto.cs
  • src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderIndexingDto.cs
  • src/Modules/Providers/Application/DTOs/ProviderDto.cs
  • src/Modules/Providers/Application/DTOs/PublicProviderDto.cs
  • src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdQueryHandler.cs
  • src/Modules/Providers/Application/Mappers/ProviderMapper.cs
  • src/Modules/Providers/Application/ModuleApi/ProvidersModuleApi.cs
  • src/Modules/Providers/Domain/Entities/Provider.cs
  • src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs
  • src/Modules/Providers/Domain/Events/ProviderRegisteredDomainEvent.cs
  • src/Modules/Providers/Domain/Repositories/IProviderRepository.cs
  • src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs
  • src/Modules/Providers/Infrastructure/Persistence/Repositories/ProviderRepository.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/GetMyProviderProfileEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/GetMyProviderStatusEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/UpdateMyProviderProfileEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/UploadMyDocumentEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/Application/Services/ProvidersModuleApiTests.cs
  • src/Modules/Providers/Tests/Unit/Domain/Entities/ProviderTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs
  • src/Modules/SearchProviders/Application/DTOs/SearchableProviderDto.cs
  • src/Modules/SearchProviders/Application/Handlers/SearchProvidersQueryHandler.cs
  • src/Modules/SearchProviders/Application/ModuleApi/SearchProvidersModuleApi.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/DTOs/ProviderSearchResultDto.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Repositories/SearchableProviderRepository.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs
  • src/Shared/Utilities/SlugHelper.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs
  • tests/MeAjudaAi.Web.Admin.Tests/Pages/ProvidersPageTests.cs

Comment on lines 139 to +140
Name = name.Trim();
Slug = SlugHelper.Generate(Name);
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 | 🟠 Major

Evite colisões de slug gerado só pelo nome.

Com o índice único em providers.slug, SlugHelper.Generate(Name) cria a mesma chave para providers com nomes repetidos ou para um rename que copie um nome já existente. Hoje o fluxo de cadastro só recupera conflito de UserId, então esse caso tende a estourar como erro genérico de banco. Precisamos gerar slug único antes de persistir ou tratar conflito de slug explicitamente na aplicação.

Also applies to: 174-189, 211-241

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

In `@src/Modules/Providers/Domain/Entities/Provider.cs` around lines 139 - 140, O
campo Slug está sendo gerado apenas a partir do nome (Slug =
SlugHelper.Generate(Name)) o que causa colisões com o índice único em
providers.slug; altere a lógica para garantir slug único antes de persistir:
crie/usar um método como EnsureUniqueSlugAsync/GenerateUniqueSlug que tenta
SlugHelper.Generate(Name) e, enquanto ProviderRepository.SlugExists(slug,
excludeId) for true, anexe um sufixo incremental ("-1", "-2", ...) ou um hash
curto até obter um slug livre, então atribua Slug ao entity; alternativamente,
ao invés de só confiar no banco, capture conflito de unique constraint durante
Save/Update e reexecute a geração/atualização do slug e re-persista; aplique
mesma correção nas outras ocorrências mencionadas (blocos em 174-189 e 211-241)
referenciando as mesmas funções e o repositório de providers.

Comment on lines +12 to 22
/// <param name="Slug">Novo slug amigável para URL</param>
/// <param name="UpdatedBy">Quem fez a atualização</param>
/// <param name="UpdatedFields">Lista dos campos que foram atualizados</param>
public record ProviderProfileUpdatedDomainEvent(
Guid AggregateId,
int Version,
string Name,
string Email,
string Slug,
string? UpdatedBy,
string[] UpdatedFields
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Objetivo: confirmar se o payload de integração e seu mapper propagam Slug.
# Esperado: existência de propriedade/campo Slug no evento de integração
# e mapeamento explícito no ToIntegrationEvent.

rg -n -C3 'ProviderProfileUpdatedIntegrationEvent|ToIntegrationEvent|UpdatedFields|Slug'

Repository: frigini/MeAjudaAi

Length of output: 50373


🏁 Script executed:

cat -n "src/Shared/Messaging/Messages/Providers/ProviderProfileUpdatedIntegrationEvent.cs"

Repository: frigini/MeAjudaAi

Length of output: 1091


🏁 Script executed:

cat -n "src/Modules/Providers/Infrastructure/Events/Mappers/ProviderEventMappers.cs" | sed -n '60,75p'

Repository: frigini/MeAjudaAi

Length of output: 826


Adicione o campo Slug ao evento de integração e mapei-o no mapper.

Slug foi adicionado ao evento de domínio, mas está ausente do ProviderProfileUpdatedIntegrationEvent e não é mapeado em ProviderEventMappers.ToIntegrationEvent(). Se "Slug" constar em UpdatedFields, consumidores (como SearchProvidersModuleApi) não receberão a atualização.

Alterações necessárias
  1. Adicione propriedade em ProviderProfileUpdatedIntegrationEvent:

    public sealed record ProviderProfileUpdatedIntegrationEvent(
        ...
        string? Slug = null,
        ...
    ) : IntegrationEvent(Source);
  2. Mapeie em ProviderEventMappers.cs:

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

In `@src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs`
around lines 12 - 22, The domain event ProviderProfileUpdatedDomainEvent now
includes Slug but the integration event ProviderProfileUpdatedIntegrationEvent
and the mapper ProviderEventMappers.ToIntegrationEvent() were not updated; add a
nullable Slug property to ProviderProfileUpdatedIntegrationEvent (e.g., string?
Slug = null) and update ProviderEventMappers.ToIntegrationEvent to set Slug:
domainEvent.Slug when constructing the integration event so consumers like
SearchProvidersModuleApi receive slug updates when "Slug" appears in
UpdatedFields.

Comment on lines +27 to +28
string Email,
string Slug
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 | 🟠 Major

Slug foi adicionado no evento de domínio, mas está sendo descartado na integração.

Hoje o ProviderRegisteredDomainEvent carrega Slug, porém src/Modules/Providers/Infrastructure/Events/Mappers/ProviderEventMappers.cs:16-27 não o mapeia e src/Shared/Messaging/Messages/Providers/ProviderRegisteredIntegrationEvent.cs:1-12 não tem esse campo. Resultado: perda de dado no publish.

🔧 Ajuste sugerido (contrato + mapper)
// src/Shared/Messaging/Messages/Providers/ProviderRegisteredIntegrationEvent.cs
 public sealed record ProviderRegisteredIntegrationEvent(
     string Source,
     Guid ProviderId,
     Guid UserId,
     string Name,
+    string Slug,
     string ProviderType,
     string Email,
     string? PhoneNumber = null,
     string? City = null,
     string? State = null,
     DateTime? RegisteredAt = null
 ) : IntegrationEvent(Source);
// src/Modules/Providers/Infrastructure/Events/Mappers/ProviderEventMappers.cs
 return new ProviderRegisteredIntegrationEvent(
     Source: ModuleName,
     ProviderId: domainEvent.AggregateId,
     UserId: domainEvent.UserId,
     Name: domainEvent.Name,
+    Slug: domainEvent.Slug,
     ProviderType: domainEvent.Type.ToString(),
     Email: domainEvent.Email,
     RegisteredAt: DateTime.UtcNow
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Providers/Domain/Events/ProviderRegisteredDomainEvent.cs` around
lines 27 - 28, O evento de domínio ProviderRegisteredDomainEvent agora inclui o
campo Slug, mas ele não está sendo propagado: atualize o contrato de integração
ProviderRegisteredIntegrationEvent para expor uma propriedade Slug (mesmo tipo
que no domínio) e ajuste o mapeador em ProviderEventMappers (a função que
transforma ProviderRegisteredDomainEvent em ProviderRegisteredIntegrationEvent)
para atribuir integrationEvent.Slug = domainEvent.Slug; garanta também que
construtores/serialização do ProviderRegisteredIntegrationEvent aceitem/definam
o Slug para evitar perda de dado ao publicar.

Comment on lines +250 to +252
builder.HasIndex(p => p.Slug)
.IsUnique()
.HasDatabaseName("ix_providers_slug");
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 | 🟠 Major

Índice único em slug pode quebrar cadastro com nomes repetidos.

Com o índice único em Line [250], qualquer colisão de slug vira erro de persistência. Como o gerador atual deriva slug apenas do nome (sem sufixo de desambiguação), dois providers com mesmo nome tendem a conflitar.

Direção de correção
// Fluxo sugerido (fora deste arquivo): resolver unicidade antes de salvar.
-var slug = SlugHelper.Generate(name);
+var baseSlug = SlugHelper.Generate(name);
+var slug = await slugUniquenessService.ResolveAsync(baseSlug, cancellationToken);
// Ex.: "acme", "acme-2", "acme-3"...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs`
around lines 250 - 252, The unique index declared by ProviderConfiguration via
builder.HasIndex(p => p.Slug).IsUnique() will cause persistence errors for
providers with duplicate names; remove the uniqueness constraint (drop
.IsUnique()) or replace it with a non-unique index, or alternatively make the
index composite (e.g., include a tenant or owner key) and/or update the slug
generation logic to append a disambiguating suffix so Slug values are unique;
locate the HasIndex(p => p.Slug) call in ProviderConfiguration and apply one of
these fixes consistently with existing slug generation code.

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