Skip to content

Commit 53943da

Browse files
author
Filipe Frigini
committed
feat(providers): implementar integração Providers ↔ ServiceCatalogs
- Criar comando e handler AddServiceToProviderCommand - Validação via IServiceCatalogsModuleApi.ValidateServicesAsync - Verifica existência e status ativo do serviço - Previne duplicação - Criar comando e handler RemoveServiceFromProviderCommand - Remove associação provider-service - Valida que serviço está sendo oferecido - Criar endpoints POST e DELETE /api/v1/providers/{providerId}/services/{serviceId} - Autorização SelfOrAdmin - Documentação OpenAPI completa - Códigos HTTP apropriados (204, 400, 404) - Adicionar Bruno collections - AddServiceToProvider.bru - RemoveServiceFromProvider.bru - Adicionar project reference ServiceCatalogs.Application ao Providers.Application - Corrigir warning S1144 em ProviderService.Provider (remover setter privado) Migrations e entities já existiam (criados previamente) Tabela provider_services com chave composta (provider_id, service_id) Eventos de domínio: ProviderServiceAddedDomainEvent, ProviderServiceRemovedDomainEvent
1 parent 3d2b260 commit 53943da

File tree

25 files changed

+525
-21
lines changed

25 files changed

+525
-21
lines changed

src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,7 @@
687687
"type": "Project",
688688
"dependencies": {
689689
"MeAjudaAi.Modules.Locations.Domain": "[1.0.0, )",
690-
"MeAjudaAi.Shared": "[1.0.0, )",
691-
"MediatR": "(, )"
690+
"MeAjudaAi.Shared": "[1.0.0, )"
692691
}
693692
},
694693
"meajudaai.modules.locations.domain": {
@@ -718,6 +717,7 @@
718717
"type": "Project",
719718
"dependencies": {
720719
"MeAjudaAi.Modules.Providers.Domain": "[1.0.0, )",
720+
"MeAjudaAi.Modules.ServiceCatalogs.Application": "[1.0.0, )",
721721
"MeAjudaAi.Shared": "[1.0.0, )"
722722
}
723723
},

src/Modules/Documents/Tests/packages.lock.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,8 +1230,7 @@
12301230
"type": "Project",
12311231
"dependencies": {
12321232
"MeAjudaAi.Modules.Locations.Domain": "[1.0.0, )",
1233-
"MeAjudaAi.Shared": "[1.0.0, )",
1234-
"MediatR": "(, )"
1233+
"MeAjudaAi.Shared": "[1.0.0, )"
12351234
}
12361235
},
12371236
"meajudaai.modules.locations.domain": {
@@ -1264,6 +1263,7 @@
12641263
"type": "Project",
12651264
"dependencies": {
12661265
"MeAjudaAi.Modules.Providers.Domain": "[1.0.0, )",
1266+
"MeAjudaAi.Modules.ServiceCatalogs.Application": "[1.0.0, )",
12671267
"MeAjudaAi.Shared": "[1.0.0, )"
12681268
}
12691269
},

src/Modules/Locations/Tests/packages.lock.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,7 @@
12411241
"type": "Project",
12421242
"dependencies": {
12431243
"MeAjudaAi.Modules.Providers.Domain": "[1.0.0, )",
1244+
"MeAjudaAi.Modules.ServiceCatalogs.Application": "[1.0.0, )",
12441245
"MeAjudaAi.Shared": "[1.0.0, )"
12451246
}
12461247
},
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
meta {
2+
name: Add Service to Provider
3+
type: http
4+
seq: 14
5+
}
6+
7+
post {
8+
url: {{baseUrl}}/api/v1/providers/{{providerId}}/services/{{serviceId}}
9+
body: none
10+
auth: bearer
11+
}
12+
13+
auth:bearer {
14+
token: {{accessToken}}
15+
}
16+
17+
headers {
18+
Accept: application/json
19+
}
20+
21+
docs {
22+
# Add Service to Provider
23+
24+
Adiciona um serviço do catálogo a um provider.
25+
26+
## Autorização
27+
- **Permissão**: SelfOrAdmin
28+
- **Requer token**: Sim
29+
30+
## Path Parameters
31+
- **providerId**: ID do provider (UUID)
32+
- **serviceId**: ID do serviço do catálogo (UUID)
33+
34+
## Validações
35+
- O serviço deve existir no catálogo
36+
- O serviço deve estar ativo
37+
- O provider não pode já oferecer o serviço
38+
- O provider não pode estar deletado
39+
40+
## Response
41+
- **204 No Content**: Serviço adicionado com sucesso
42+
- **400 Bad Request**: Serviço inválido, inativo ou já adicionado
43+
- **404 Not Found**: Provider não encontrado
44+
45+
## Eventos de Domínio
46+
- **ProviderServiceAddedDomainEvent**: Emitido após adição bem-sucedida
47+
48+
## Integração
49+
- Valida serviço via **IServiceCatalogsModuleApi.ValidateServicesAsync**
50+
- Verifica existência e status (ativo/inativo)
51+
}
52+
53+
vars:post-response {
54+
res: res.status
55+
}
56+
57+
assert {
58+
res.status: eq 204
59+
}
60+
61+
tests {
62+
test("Status code is 204", function() {
63+
expect(res.getStatus()).to.equal(204);
64+
});
65+
66+
test("Service added successfully", function() {
67+
console.log("Service added to provider: " + bru.getEnvVar("providerId"));
68+
});
69+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
meta {
2+
name: Remove Service from Provider
3+
type: http
4+
seq: 15
5+
}
6+
7+
delete {
8+
url: {{baseUrl}}/api/v1/providers/{{providerId}}/services/{{serviceId}}
9+
body: none
10+
auth: bearer
11+
}
12+
13+
auth:bearer {
14+
token: {{accessToken}}
15+
}
16+
17+
headers {
18+
Accept: application/json
19+
}
20+
21+
docs {
22+
# Remove Service from Provider
23+
24+
Remove um serviço do catálogo de um provider.
25+
26+
## Autorização
27+
- **Permissão**: SelfOrAdmin
28+
- **Requer token**: Sim
29+
30+
## Path Parameters
31+
- **providerId**: ID do provider (UUID)
32+
- **serviceId**: ID do serviço do catálogo (UUID)
33+
34+
## Validações
35+
- O provider deve existir
36+
- O provider deve oferecer o serviço
37+
- O provider não pode estar deletado
38+
39+
## Response
40+
- **204 No Content**: Serviço removido com sucesso
41+
- **400 Bad Request**: Serviço não é oferecido pelo provider
42+
- **404 Not Found**: Provider não encontrado
43+
44+
## Eventos de Domínio
45+
- **ProviderServiceRemovedDomainEvent**: Emitido após remoção bem-sucedida
46+
}
47+
48+
vars:delete-response {
49+
res: res.status
50+
}
51+
52+
assert {
53+
res.status: eq 204
54+
}
55+
56+
tests {
57+
test("Status code is 204", function() {
58+
expect(res.getStatus()).to.equal(204);
59+
});
60+
61+
test("Service removed successfully", function() {
62+
console.log("Service removed from provider: " + bru.getEnvVar("providerId"));
63+
});
64+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using MeAjudaAi.Modules.Providers.Application.Commands;
2+
using MeAjudaAi.Shared.Commands;
3+
using MeAjudaAi.Shared.Endpoints;
4+
using MeAjudaAi.Shared.Functional;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.Mvc;
8+
using Microsoft.AspNetCore.Routing;
9+
10+
namespace MeAjudaAi.Modules.Providers.API.Endpoints.ProviderServices;
11+
12+
/// <summary>
13+
/// Endpoint para adicionar um serviço do catálogo a um provider.
14+
/// </summary>
15+
public class AddServiceToProviderEndpoint : BaseEndpoint
16+
{
17+
public static void Map(IEndpointRouteBuilder app)
18+
=> app.MapPost("/api/v1/providers/{providerId:guid}/services/{serviceId:guid}", AddServiceAsync)
19+
.WithName("AddServiceToProvider")
20+
.WithTags("Providers - Services")
21+
.WithSummary("Adiciona serviço ao provider")
22+
.WithDescription("""
23+
### Adiciona um serviço do catálogo ao provider
24+
25+
**Funcionalidades:**
26+
- ✅ Valida existência e status do serviço via IServiceCatalogsModuleApi
27+
- ✅ Verifica se o serviço está ativo
28+
- ✅ Previne duplicação de serviços
29+
- ✅ Emite evento de domínio ProviderServiceAddedDomainEvent
30+
31+
**Campos obrigatórios:**
32+
- providerId: ID do provider (UUID)
33+
- serviceId: ID do serviço do catálogo (UUID)
34+
35+
**Validações:**
36+
- Serviço deve existir no catálogo
37+
- Serviço deve estar ativo
38+
- Provider não pode já oferecer o serviço
39+
- Provider não pode estar deletado
40+
""")
41+
.Produces(StatusCodes.Status204NoContent)
42+
.Produces(StatusCodes.Status400BadRequest)
43+
.Produces(StatusCodes.Status404NotFound)
44+
.RequireAuthorization("SelfOrAdmin");
45+
46+
/// <summary>
47+
/// Processa requisição de adição de serviço ao provider.
48+
/// </summary>
49+
private static async Task<IResult> AddServiceAsync(
50+
[FromRoute] Guid providerId,
51+
[FromRoute] Guid serviceId,
52+
ICommandDispatcher commandDispatcher,
53+
CancellationToken cancellationToken)
54+
{
55+
var command = new AddServiceToProviderCommand(providerId, serviceId);
56+
var result = await commandDispatcher.SendAsync<AddServiceToProviderCommand, Result>(command, cancellationToken);
57+
58+
return result.IsSuccess
59+
? Results.NoContent()
60+
: Handle(result);
61+
}
62+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using MeAjudaAi.Modules.Providers.Application.Commands;
2+
using MeAjudaAi.Shared.Commands;
3+
using MeAjudaAi.Shared.Endpoints;
4+
using MeAjudaAi.Shared.Functional;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.Mvc;
8+
using Microsoft.AspNetCore.Routing;
9+
10+
namespace MeAjudaAi.Modules.Providers.API.Endpoints.ProviderServices;
11+
12+
/// <summary>
13+
/// Endpoint para remover um serviço do catálogo de um provider.
14+
/// </summary>
15+
public class RemoveServiceFromProviderEndpoint : BaseEndpoint
16+
{
17+
public static void Map(IEndpointRouteBuilder app)
18+
=> app.MapDelete("/api/v1/providers/{providerId:guid}/services/{serviceId:guid}", RemoveServiceAsync)
19+
.WithName("RemoveServiceFromProvider")
20+
.WithTags("Providers - Services")
21+
.WithSummary("Remove serviço do provider")
22+
.WithDescription("""
23+
### Remove um serviço do catálogo do provider
24+
25+
**Funcionalidades:**
26+
- ✅ Remove associação entre provider e serviço
27+
- ✅ Emite evento de domínio ProviderServiceRemovedDomainEvent
28+
- ✅ Valida que o provider oferece o serviço antes de remover
29+
30+
**Campos obrigatórios:**
31+
- providerId: ID do provider (UUID)
32+
- serviceId: ID do serviço do catálogo (UUID)
33+
34+
**Validações:**
35+
- Provider deve existir
36+
- Provider deve oferecer o serviço
37+
- Provider não pode estar deletado
38+
""")
39+
.Produces(StatusCodes.Status204NoContent)
40+
.Produces(StatusCodes.Status400BadRequest)
41+
.Produces(StatusCodes.Status404NotFound)
42+
.RequireAuthorization("SelfOrAdmin");
43+
44+
/// <summary>
45+
/// Processa requisição de remoção de serviço do provider.
46+
/// </summary>
47+
private static async Task<IResult> RemoveServiceAsync(
48+
[FromRoute] Guid providerId,
49+
[FromRoute] Guid serviceId,
50+
ICommandDispatcher commandDispatcher,
51+
CancellationToken cancellationToken)
52+
{
53+
var command = new RemoveServiceFromProviderCommand(providerId, serviceId);
54+
var result = await commandDispatcher.SendAsync<RemoveServiceFromProviderCommand, Result>(command, cancellationToken);
55+
56+
return result.IsSuccess
57+
? Results.NoContent()
58+
: Handle(result);
59+
}
60+
}

src/Modules/Providers/API/packages.lock.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@
484484
"type": "Project",
485485
"dependencies": {
486486
"MeAjudaAi.Modules.Providers.Domain": "[1.0.0, )",
487+
"MeAjudaAi.Modules.ServiceCatalogs.Application": "[1.0.0, )",
487488
"MeAjudaAi.Shared": "[1.0.0, )"
488489
}
489490
},
@@ -502,6 +503,19 @@
502503
"MeAjudaAi.Shared": "[1.0.0, )"
503504
}
504505
},
506+
"meajudaai.modules.servicecatalogs.application": {
507+
"type": "Project",
508+
"dependencies": {
509+
"MeAjudaAi.Modules.ServiceCatalogs.Domain": "[1.0.0, )",
510+
"MeAjudaAi.Shared": "[1.0.0, )"
511+
}
512+
},
513+
"meajudaai.modules.servicecatalogs.domain": {
514+
"type": "Project",
515+
"dependencies": {
516+
"MeAjudaAi.Shared": "[1.0.0, )"
517+
}
518+
},
505519
"meajudaai.shared": {
506520
"type": "Project",
507521
"dependencies": {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using MeAjudaAi.Shared.Commands;
2+
using MeAjudaAi.Shared.Functional;
3+
4+
namespace MeAjudaAi.Modules.Providers.Application.Commands;
5+
6+
/// <summary>
7+
/// Comando para adicionar um serviço do catálogo a um provider.
8+
/// </summary>
9+
/// <param name="ProviderId">ID do provider</param>
10+
/// <param name="ServiceId">ID do serviço do catálogo (módulo ServiceCatalogs)</param>
11+
public sealed record AddServiceToProviderCommand(
12+
Guid ProviderId,
13+
Guid ServiceId
14+
) : Command<Result>;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using MeAjudaAi.Shared.Commands;
2+
using MeAjudaAi.Shared.Functional;
3+
4+
namespace MeAjudaAi.Modules.Providers.Application.Commands;
5+
6+
/// <summary>
7+
/// Comando para remover um serviço do catálogo de um provider.
8+
/// </summary>
9+
/// <param name="ProviderId">ID do provider</param>
10+
/// <param name="ServiceId">ID do serviço do catálogo (módulo ServiceCatalogs)</param>
11+
public sealed record RemoveServiceFromProviderCommand(
12+
Guid ProviderId,
13+
Guid ServiceId
14+
) : Command<Result>;

0 commit comments

Comments
 (0)