Merge branch 'main' of https://github.com/bit365/hello-shop
This commit is contained in:
commit
3b631e495f
@ -1,4 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.DatabaseIsolationService.Entities;
|
||||
using MultiTenancySample.DatabaseIsolationService.EntityFrameworks;
|
||||
@ -23,5 +26,14 @@ namespace MultiTenancySample.DatabaseIsolationService.Controllers
|
||||
|
||||
return Ok(products);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateProduct(Product product)
|
||||
{
|
||||
await _dbContext.AddAsync(product);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(product);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
namespace MultiTenancySample.DatabaseIsolationService.Entities
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
namespace MultiTenancySample.DatabaseIsolationService.Entities
|
||||
{
|
||||
public class Product
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.DatabaseIsolationService.Entities;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.DatabaseIsolationService.Entities;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using MultiTenancySample.DatabaseIsolationService.EntityFrameworks;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.FieldIsolationService.Entities;
|
||||
using MultiTenancySample.FieldIsolationService.EntityFrameworks;
|
||||
@ -23,5 +26,15 @@ namespace MultiTenancySample.FieldIsolationService.Controllers
|
||||
|
||||
return Ok(products);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> CreateProduct(Product product)
|
||||
{
|
||||
await _dbContext.AddAsync(product);
|
||||
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
return Ok(product);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
namespace MultiTenancySample.FieldIsolationService.Entities
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
namespace MultiTenancySample.FieldIsolationService.Entities
|
||||
{
|
||||
public interface IMultiTenant
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
using System.Text.Json.Serialization;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MultiTenancySample.FieldIsolationService.Entities
|
||||
{
|
||||
|
@ -1,16 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.FieldIsolationService.Entities;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
||||
namespace MultiTenancySample.FieldIsolationService.EntityFrameworks
|
||||
{
|
||||
public class DataSeeding(IDbContextFactory<FieldIsolationServiceDbContext> dbContext, ICurrentTenant currentTenant)
|
||||
public class DataSeeding(IDbContextFactory<FieldIsolationServiceDbContext> dbContextFactory, ICurrentTenant currentTenant)
|
||||
{
|
||||
public async Task SeedDataAsync()
|
||||
{
|
||||
currentTenant.SetTenant("Tenant1");
|
||||
|
||||
FieldIsolationServiceDbContext dbContext1 = await dbContext.CreateDbContextAsync();
|
||||
FieldIsolationServiceDbContext dbContext1 = await dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
if (await dbContext1.Database.EnsureCreatedAsync())
|
||||
{
|
||||
@ -25,7 +28,7 @@ namespace MultiTenancySample.FieldIsolationService.EntityFrameworks
|
||||
|
||||
currentTenant.SetTenant("Tenant2");
|
||||
|
||||
FieldIsolationServiceDbContext dbContext2 = await dbContext.CreateDbContextAsync();
|
||||
FieldIsolationServiceDbContext dbContext2 = await dbContextFactory.CreateDbContextAsync();
|
||||
|
||||
if (await dbContext2.Set<Product>().IgnoreQueryFilters().CountAsync() < 6)
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using MultiTenancySample.FieldIsolationService.Entities;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using MultiTenancySample.FieldIsolationService.Entities;
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.FieldIsolationService.EntityFrameworks;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.SchemaIsolationService.Entities;
|
||||
using MultiTenancySample.SchemaIsolationService.EntityFrameworks;
|
||||
|
@ -1,4 +1,7 @@
|
||||
namespace MultiTenancySample.SchemaIsolationService.Entities
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
namespace MultiTenancySample.SchemaIsolationService.Entities
|
||||
{
|
||||
public class Product
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.SchemaIsolationService.Entities;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.SchemaIsolationService.Entities;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MultiTenancySample.SchemaIsolationService.EntityFrameworks;
|
||||
using MultiTenancySample.ServiceDefaults;
|
||||
|
@ -1,4 +1,7 @@
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
{
|
||||
public class CurrentTenant : ICurrentTenant
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
{
|
||||
public interface ICurrentTenant
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
{
|
||||
public interface ITenantIdProvider
|
||||
{
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
{
|
||||
@ -10,6 +13,11 @@ namespace MultiTenancySample.ServiceDefaults
|
||||
|
||||
const string tenantKey = "tenant";
|
||||
|
||||
if (httpContext.User.FindAll(tenantKey).Any())
|
||||
{
|
||||
return httpContext.User.FindFirst(tenantKey)?.Value;
|
||||
}
|
||||
|
||||
if (httpContext.Request.Headers.TryGetValue(tenantKey, out var headerValues))
|
||||
{
|
||||
return headerValues.First();
|
||||
|
@ -1,4 +1,7 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// See the license file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MultiTenancySample.ServiceDefaults
|
||||
{
|
||||
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.204",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
@ -21,7 +21,8 @@ public class OpenApiConfigureOptions(IConfiguredServiceEndPointResolver serviceR
|
||||
|
||||
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)
|
||||
{
|
||||
urlDescriptors.Add(new UrlDescriptor
|
||||
|
@ -9,6 +9,6 @@
|
||||
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery.Yarp" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery.Yarp" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -15,6 +15,6 @@
|
||||
<ProjectReference Include="..\HelloShop.WebApp\HelloShop.WebApp.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.1" />
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
</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,14 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IncludeHttpRuleProtos>true</IncludeHttpRuleProtos>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Protos\basket.proto" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\basket.proto" GrpcServices="Server" />
|
||||
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
|
||||
</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" />
|
||||
|
@ -1,22 +1,61 @@
|
||||
// Copyright (c) HelloShop Corporation. All rights reserved.
|
||||
// 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.ServiceDefaults.Extensions;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.AddServiceDefaults();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddGrpc();
|
||||
// Add extensions services to the container.
|
||||
|
||||
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();
|
||||
|
||||
app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
|
||||
app.MapDefaultEndpoints();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
app.MapGrpcService<GreeterService>();
|
||||
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");
|
||||
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client.").WithTags("Welcome");
|
||||
|
||||
// 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();
|
||||
|
@ -4,7 +4,8 @@
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:8004",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
@ -13,7 +14,8 @@
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:8104;http://localhost:8004",
|
||||
"environmentVariables": {
|
||||
"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";
|
||||
|
||||
option csharp_namespace = "HelloShop.BasketService";
|
||||
option csharp_namespace = "HelloShop.BasketService.Protos";
|
||||
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
package greet;
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// 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.
|
||||
|
@ -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.
|
||||
|
||||
using Grpc.Core;
|
||||
using HelloShop.BasketService.Protos;
|
||||
|
||||
namespace HelloShop.BasketService.Services;
|
||||
|
||||
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)
|
||||
{
|
||||
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"
|
||||
}
|
||||
},
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http2"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
@ -58,9 +58,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.61" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.61" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.61" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.70" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.70" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.70" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -8,9 +8,9 @@
|
||||
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
@ -8,8 +8,8 @@
|
||||
<ProjectReference Include="..\HelloShop.ServiceDefaults\HelloShop.ServiceDefaults.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.7">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
@ -22,7 +22,7 @@ namespace HelloShop.ServiceDefaults.Extensions
|
||||
|
||||
services.Configure<SwaggerUIOptions>(options =>
|
||||
{
|
||||
options.DocumentTitle = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
options.DocumentTitle = Assembly.GetEntryAssembly()?.GetName().Name;
|
||||
options.InjectStylesheet("/ServiceDefaults/Resources/OpenApi/Custom.css");
|
||||
});
|
||||
|
||||
|
@ -7,8 +7,8 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.6.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.2" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
|
||||
|
@ -9,14 +9,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<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">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.8.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -21,7 +21,6 @@ namespace HelloShop.ProductService.FunctionalTests
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -14,11 +14,11 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.8.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -14,11 +14,11 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</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="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="xunit" Version="2.8.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
Loading…
Reference in New Issue
Block a user