使用 gRPC 实现购物车服务
This commit is contained in:
parent
49f2796cc2
commit
c192d209fd
@ -21,7 +21,8 @@ public class OpenApiConfigureOptions(IConfiguredServiceEndPointResolver serviceR
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = httpClient.GetAsync(uriBuilder.Uri).GetAwaiter().GetResult();
|
HttpRequestMessage request = new(HttpMethod.Get, uriBuilder.Uri) { Version = new Version(2, 0) };
|
||||||
|
HttpResponseMessage response = httpClient.SendAsync(request).GetAwaiter().GetResult();
|
||||||
if (response.IsSuccessStatusCode)
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
urlDescriptors.Add(new UrlDescriptor
|
urlDescriptors.Add(new UrlDescriptor
|
||||||
|
@ -9,6 +9,6 @@
|
|||||||
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery.Yarp" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery.Yarp" Version="8.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -15,6 +15,6 @@
|
|||||||
<ProjectReference Include="..\HelloShop.WebApp\HelloShop.WebApp.csproj" />
|
<ProjectReference Include="..\HelloShop.WebApp\HelloShop.WebApp.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.1" />
|
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using AutoMapper;
|
||||||
|
using HelloShop.BasketService.Entities;
|
||||||
|
using HelloShop.BasketService.Protos;
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.AutoMapper
|
||||||
|
{
|
||||||
|
public class BasketsMapConfiguration : Profile
|
||||||
|
{
|
||||||
|
public BasketsMapConfiguration()
|
||||||
|
{
|
||||||
|
CreateMap<CustomerBasket, CustomerBasketResponse>();
|
||||||
|
CreateMap<BasketItem, BasketListItem>().ReverseMap();
|
||||||
|
CreateMap<UpdateBasketRequest, CustomerBasket>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
src/HelloShop.BasketService/Entities/BasketItem.cs
Normal file
12
src/HelloShop.BasketService/Entities/BasketItem.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Entities
|
||||||
|
{
|
||||||
|
public class BasketItem
|
||||||
|
{
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
src/HelloShop.BasketService/Entities/CustomerBasket.cs
Normal file
12
src/HelloShop.BasketService/Entities/CustomerBasket.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Entities
|
||||||
|
{
|
||||||
|
public class CustomerBasket
|
||||||
|
{
|
||||||
|
public int BuyerId { get; set; }
|
||||||
|
|
||||||
|
public List<BasketItem> Items { get; set; } = [];
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,24 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
<IncludeHttpRuleProtos>true</IncludeHttpRuleProtos>
|
||||||
<ItemGroup>
|
</PropertyGroup>
|
||||||
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
|
<ItemGroup>
|
||||||
</ItemGroup>
|
<None Remove="Protos\basket.proto" />
|
||||||
<ItemGroup>
|
</ItemGroup>
|
||||||
<PackageReference Include="Grpc.AspNetCore" Version="2.63.0" />
|
<ItemGroup>
|
||||||
</ItemGroup>
|
<Protobuf Include="Protos\basket.proto" GrpcServices="Server" />
|
||||||
<ItemGroup>
|
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
|
||||||
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
</ItemGroup>
|
||||||
</ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Calzolari.Grpc.AspNetCore.Validation" Version="8.1.0" />
|
||||||
|
<PackageReference Include="Grpc.AspNetCore" Version="2.63.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Grpc.Swagger" Version="0.8.7" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,22 +1,61 @@
|
|||||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
// See the license file in the project root for more information.
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using Calzolari.Grpc.AspNetCore.Validation;
|
||||||
|
using HelloShop.BasketService.Repositories;
|
||||||
using HelloShop.BasketService.Services;
|
using HelloShop.BasketService.Services;
|
||||||
using HelloShop.ServiceDefaults.Extensions;
|
using HelloShop.ServiceDefaults.Extensions;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
// Add services to the container.
|
// Add extensions services to the container.
|
||||||
builder.Services.AddGrpc();
|
|
||||||
|
const string issuerSigningKey = HelloShop.ServiceDefaults.Constants.IdentityConstants.IssuerSigningKey;
|
||||||
|
|
||||||
|
builder.Services.AddAuthentication().AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters.ValidateIssuer = false;
|
||||||
|
options.TokenValidationParameters.ValidateAudience = false;
|
||||||
|
options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.Default.GetBytes(issuerSigningKey));
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddHttpContextAccessor();
|
||||||
|
builder.Services.AddDistributedMemoryCache();
|
||||||
|
builder.Services.AddSingleton<IBasketRepository, DistributedCacheBasketRepository>();
|
||||||
|
|
||||||
|
builder.Services.AddGrpc(options => options.EnableMessageValidation()).AddJsonTranscoding();
|
||||||
|
builder.Services.AddGrpcSwagger();
|
||||||
|
builder.Services.AddOpenApi();
|
||||||
|
|
||||||
|
builder.Services.AddDataSeedingProviders();
|
||||||
|
builder.Services.AddCustomLocalization();
|
||||||
|
builder.Services.AddModelMapper().AddModelValidator();
|
||||||
|
builder.Services.AddLocalization().AddPermissionDefinitions();
|
||||||
|
builder.Services.AddAuthorization().AddRemotePermissionChecker().AddCustomAuthorization();
|
||||||
|
builder.Services.AddGrpcValidation();
|
||||||
|
builder.Services.AddCors();
|
||||||
|
// End addd extensions services to the container.
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||||
|
|
||||||
app.MapDefaultEndpoints();
|
app.MapDefaultEndpoints();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
app.MapGrpcService<GreeterService>();
|
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client.").WithTags("Welcome");
|
||||||
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
|
|
||||||
|
|
||||||
|
// Configure extensions request pipeline.
|
||||||
|
app.UseAuthentication().UseAuthorization();
|
||||||
|
app.MapGrpcService<GreeterService>();
|
||||||
|
app.MapGrpcService<CustomerBasketService>();
|
||||||
|
app.UseDataSeedingProviders();
|
||||||
|
app.UseCustomLocalization();
|
||||||
|
app.UseOpenApi();
|
||||||
|
app.MapGroup("api/Permissions").MapPermissionDefinitions("Permissions");
|
||||||
|
// End configure extensions request pipeline.
|
||||||
app.Run();
|
app.Run();
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
"http": {
|
"http": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
"applicationUrl": "http://localhost:8004",
|
"applicationUrl": "http://localhost:8004",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
@ -13,7 +14,8 @@
|
|||||||
"https": {
|
"https": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
"applicationUrl": "https://localhost:8104;http://localhost:8004",
|
"applicationUrl": "https://localhost:8104;http://localhost:8004",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
42
src/HelloShop.BasketService/Protos/basket.proto
Normal file
42
src/HelloShop.BasketService/Protos/basket.proto
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "google/protobuf/empty.proto";
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
|
||||||
|
option csharp_namespace = "HelloShop.BasketService.Protos";
|
||||||
|
|
||||||
|
package basket;
|
||||||
|
|
||||||
|
service Basket {
|
||||||
|
rpc GetBasket(google.protobuf.Empty) returns (CustomerBasketResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/api/basket"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc UpdateBasket(UpdateBasketRequest) returns (CustomerBasketResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
put: "/api/basket"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc DeleteBasket(google.protobuf.Empty) returns (google.protobuf.Empty) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
delete: "/api/basket"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message CustomerBasketResponse {
|
||||||
|
repeated BasketListItem items = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BasketListItem {
|
||||||
|
int32 product_id = 1;
|
||||||
|
int32 quantity = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateBasketRequest {
|
||||||
|
repeated BasketListItem items = 1;
|
||||||
|
}
|
@ -1,13 +1,20 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
option csharp_namespace = "HelloShop.BasketService";
|
option csharp_namespace = "HelloShop.BasketService.Protos";
|
||||||
|
|
||||||
|
import "google/api/annotations.proto";
|
||||||
|
|
||||||
package greet;
|
package greet;
|
||||||
|
|
||||||
// The greeting service definition.
|
// The greeting service definition.
|
||||||
service Greeter {
|
service Greeter {
|
||||||
// Sends a greeting
|
// Sends a greeting
|
||||||
rpc SayHello (HelloRequest) returns (HelloReply);
|
rpc SayHello (HelloRequest) returns (HelloReply){
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/api/greet"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The request message containing the user's name.
|
// The request message containing the user's name.
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.BasketService.Entities;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Repositories
|
||||||
|
{
|
||||||
|
public class DistributedCacheBasketRepository(IDistributedCache cache) : IBasketRepository
|
||||||
|
{
|
||||||
|
private const string BasketKeyPrefix = "basket";
|
||||||
|
|
||||||
|
private static string GetBasketKey(int customerId) => $"{BasketKeyPrefix}:{customerId}";
|
||||||
|
|
||||||
|
public async Task DeleteBasketAsync(int customerId, CancellationToken token = default) => await cache.RemoveAsync(GetBasketKey(customerId), token);
|
||||||
|
|
||||||
|
public async Task<CustomerBasket?> GetBasketAsync(int customerId, CancellationToken token = default) => await cache.GetObjectAsync<CustomerBasket>(GetBasketKey(customerId), token);
|
||||||
|
|
||||||
|
public async Task<CustomerBasket?> UpdateBasketAsync(CustomerBasket basket, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
DistributedCacheEntryOptions options = new() { SlidingExpiration = TimeSpan.MaxValue };
|
||||||
|
|
||||||
|
await cache.SetObjectAsync(GetBasketKey(basket.BuyerId), basket, options, token);
|
||||||
|
|
||||||
|
return await GetBasketAsync(basket.BuyerId, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.BasketService.Entities;
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Repositories
|
||||||
|
{
|
||||||
|
public interface IBasketRepository
|
||||||
|
{
|
||||||
|
Task<CustomerBasket?> GetBasketAsync(int customerId, CancellationToken token = default);
|
||||||
|
|
||||||
|
Task<CustomerBasket?> UpdateBasketAsync(CustomerBasket basket, CancellationToken token = default);
|
||||||
|
|
||||||
|
Task DeleteBasketAsync(int customerId, CancellationToken token = default);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using AutoMapper;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
using Grpc.Core;
|
||||||
|
using HelloShop.BasketService.Entities;
|
||||||
|
|
||||||
|
|
||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using HelloShop.BasketService.Protos;
|
||||||
|
using HelloShop.BasketService.Repositories;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Services
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class CustomerBasketService(IBasketRepository repository, ILogger<CustomerBasketService> logger, IMapper mapper) : Basket.BasketBase
|
||||||
|
{
|
||||||
|
public override async Task<CustomerBasketResponse> GetBasket(Empty request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
string? nameIdentifier = context.GetHttpContext().User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
|
if (!int.TryParse(nameIdentifier, out int userId))
|
||||||
|
{
|
||||||
|
throw new RpcException(new Status(StatusCode.Unauthenticated, "The caller is not authenticated."));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("Begin GetBasketById call from method {Method} for basket id {Id}", context.Method, userId);
|
||||||
|
|
||||||
|
CustomerBasket? basket = await repository.GetBasketAsync(userId, context.CancellationToken);
|
||||||
|
|
||||||
|
if (basket is not null)
|
||||||
|
{
|
||||||
|
mapper.Map<CustomerBasketResponse>(basket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<CustomerBasketResponse> UpdateBasket(UpdateBasketRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
string? nameIdentifier = context.GetHttpContext().User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
|
if (!int.TryParse(nameIdentifier, out int userId))
|
||||||
|
{
|
||||||
|
throw new RpcException(new Status(StatusCode.Unauthenticated, "The caller is not authenticated."));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug("Begin UpdateBasket call from method {Method} for basket id {Id}", context.Method, userId);
|
||||||
|
|
||||||
|
CustomerBasket customerBasket = mapper.Map<CustomerBasket>(request, opts => opts.AfterMap((src, dest) => dest.BuyerId = userId));
|
||||||
|
|
||||||
|
CustomerBasket? result = await repository.UpdateBasketAsync(customerBasket, context.CancellationToken);
|
||||||
|
|
||||||
|
result = result ?? throw new RpcException(new Status(StatusCode.NotFound, $"Basket with buyer id {userId} does not exist"));
|
||||||
|
|
||||||
|
return mapper.Map<CustomerBasketResponse>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Empty> DeleteBasket(Empty request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
string? nameIdentifier = context.GetHttpContext().User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
|
|
||||||
|
if (!int.TryParse(nameIdentifier, out int userId))
|
||||||
|
{
|
||||||
|
throw new RpcException(new Status(StatusCode.Unauthenticated, "The caller is not authenticated."));
|
||||||
|
}
|
||||||
|
|
||||||
|
await repository.DeleteBasketAsync(userId, context.CancellationToken);
|
||||||
|
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,17 +2,12 @@
|
|||||||
// See the license file in the project root for more information.
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
|
using HelloShop.BasketService.Protos;
|
||||||
|
|
||||||
namespace HelloShop.BasketService.Services;
|
namespace HelloShop.BasketService.Services;
|
||||||
|
|
||||||
public class GreeterService : Greeter.GreeterBase
|
public class GreeterService : Greeter.GreeterBase
|
||||||
{
|
{
|
||||||
private readonly ILogger<GreeterService> _logger;
|
|
||||||
public GreeterService(ILogger<GreeterService> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
|
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
return Task.FromResult(new HelloReply
|
return Task.FromResult(new HelloReply
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using FluentValidation;
|
||||||
|
using FluentValidation.Validators;
|
||||||
|
using HelloShop.BasketService.Protos;
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Validations.Baskets
|
||||||
|
{
|
||||||
|
public class BasketListItemValidator : AbstractValidator<BasketListItem>
|
||||||
|
{
|
||||||
|
public BasketListItemValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.ProductId).GreaterThan(0);
|
||||||
|
RuleFor(x => x.Quantity).GreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using FluentValidation;
|
||||||
|
using HelloShop.BasketService.Protos;
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Validations.Baskets
|
||||||
|
{
|
||||||
|
public class UpdateBasketRequestValidator : AbstractValidator<UpdateBasketRequest>
|
||||||
|
{
|
||||||
|
public UpdateBasketRequestValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Items).NotNull();
|
||||||
|
RuleForEach(x => x.Items).SetValidator(new BasketListItemValidator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||||
|
// See the license file in the project root for more information.
|
||||||
|
|
||||||
|
using FluentValidation;
|
||||||
|
using HelloShop.BasketService.Protos;
|
||||||
|
|
||||||
|
namespace HelloShop.BasketService.Validations.Greeters
|
||||||
|
{
|
||||||
|
public class HelloRequestValidator : AbstractValidator<HelloRequest>
|
||||||
|
{
|
||||||
|
public HelloRequestValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Name).NotNull().NotEmpty().Length(3, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,5 +5,10 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Kestrel": {
|
||||||
|
"EndpointDefaults": {
|
||||||
|
"Protocols": "Http2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
}
|
}
|
@ -58,9 +58,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.61" />
|
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.70" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.61" />
|
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.70" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.61" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.70" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -22,7 +22,7 @@ namespace HelloShop.ServiceDefaults.Extensions
|
|||||||
|
|
||||||
services.Configure<SwaggerUIOptions>(options =>
|
services.Configure<SwaggerUIOptions>(options =>
|
||||||
{
|
{
|
||||||
options.DocumentTitle = Assembly.GetExecutingAssembly().GetName().Name;
|
options.DocumentTitle = Assembly.GetEntryAssembly()?.GetName().Name;
|
||||||
options.InjectStylesheet("/ServiceDefaults/Resources/OpenApi/Custom.css");
|
options.InjectStylesheet("/ServiceDefaults/Resources/OpenApi/Custom.css");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.6.0" />
|
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.7.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.2" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
|
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Aspire.Hosting.Testing" Version="8.0.1" />
|
<PackageReference Include="Aspire.Hosting.Testing" Version="8.0.2" />
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
<PackageReference Include="xunit" Version="2.8.1" />
|
<PackageReference Include="xunit" Version="2.9.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
<PackageReference Include="xunit" Version="2.8.1" />
|
<PackageReference Include="xunit" Version="2.9.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
<PackageReference Include="Moq" Version="4.20.70" />
|
<PackageReference Include="Moq" Version="4.20.70" />
|
||||||
<PackageReference Include="xunit" Version="2.8.1" />
|
<PackageReference Include="xunit" Version="2.9.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
Loading…
Reference in New Issue
Block a user