Browse Source

Merge branch 'main' into gterdem/docker-compose

pull/104/head
Galip Tolga Erdem 5 years ago
parent
commit
0e6f62691a
  1. 21
      README.md
  2. BIN
      docs/images/ssl-error.png
  3. BIN
      docs/roadmap/Phase_1.png
  4. 9
      services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/AdministrationServiceHttpApiHostModule.cs
  5. 13
      services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/Controllers/HomeController.cs
  6. 33
      services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/DbMigrations/AdministrationServiceDatabaseMigrationChecker.cs
  7. 133
      services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/DbMigrations/AdministrationServiceDatabaseMigrationEventHandler.cs
  8. 273
      services/basket/src/EShopOnAbp.BasketService/BasketServiceModule.cs
  9. 9
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/CatalogServiceHttpApiHostModule.cs
  10. 13
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/Controllers/HomeController.cs
  11. 49
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/DbMigrations/CatalogServiceDatabaseMigrationChecker.cs
  12. 83
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/DbMigrations/CatalogServiceDatabaseMigrationEventHandler.cs
  13. 172
      services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/DbMigrations/ProductServiceDataSeeder.cs
  14. 2
      services/catalog/src/EShopOnAbp.CatalogService.MongoDB/MongoDB/CatalogServiceMongoDbContext.cs
  15. 13
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/Controllers/HomeController.cs
  16. 11
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/ApplyDatabaseSeedsEto.cs
  17. 28
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/DataSeederEventHandler.cs
  18. 626
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServerDataSeeder.cs
  19. 53
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServiceDatabaseMigrationChecker.cs
  20. 142
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServiceDatabaseMigrationEventHandler.cs
  21. 9
      services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs
  22. 7
      services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/OrderDto.cs
  23. 15
      services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs
  24. 9
      services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderStatus.cs
  25. 12
      services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/Order.cs
  26. 48
      services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/OrderStatus.cs
  27. 37
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/EntityFrameworkCore/OrderServiceDataSeedContributor.cs
  28. 41
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/EntityFrameworkCore/OrderingServiceDbContext.cs
  29. 182
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/20220317123922_RemovedOrderStatusTable.Designer.cs
  30. 73
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/20220317123922_RemovedOrderStatusTable.cs
  31. 36
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/OrderingServiceDbContextModelSnapshot.cs
  32. 14
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs
  33. 3
      services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/OrderEfCoreQueryableExtensions.cs
  34. 13
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/Controllers/HomeController.cs
  35. 33
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/DbMigrations/OrderingServiceDatabaseMigrationChecker.cs
  36. 88
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/DbMigrations/OrderingServiceDatabaseMigrationEventHandler.cs
  37. 3
      services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/OrderingServiceHttpApiHostModule.cs
  38. 19
      services/ordering/test/EShopOnAbp.OrderingService.EntityFrameworkCore.Tests/EntityFrameworkCore/Orders/OrderRepository_Tests.cs
  39. 13
      services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/Controllers/HomeController.cs
  40. 33
      services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/DbMigrations/PaymentServiceDatabaseMigrationChecker.cs
  41. 76
      services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/DbMigrations/PaymentServiceDatabaseMigrationEventHandler.cs
  42. 3
      services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/PaymentServiceHttpApiHostModule.cs
  43. 19
      shared/EShopOnAbp.Shared.Hosting.AspNetCore/EShopOnAbpSharedHostingAspNetCoreModule.cs
  44. 55
      shared/EShopOnAbp.Shared.Hosting.AspNetCore/SerilogConfigurationHelper.cs
  45. 55
      shared/EShopOnAbp.Shared.Hosting.AspNetCore/SwaggerConfigurationHelper.cs
  46. 111
      shared/EShopOnAbp.Shared.Hosting.Microservices/DbMigrations/EfCore/DatabaseEfCoreMigrationEventHandler.cs
  47. 129
      shared/EShopOnAbp.Shared.Hosting.Microservices/DbMigrations/MongoDb/DatabaseMongoDbMigrationEventHandler.cs
  48. 7
      shared/EShopOnAbp.Shared.Hosting.Microservices/DbMigrations/PendingMigrationsCheckerBase.cs
  49. 1
      shared/EShopOnAbp.Shared.Hosting.Microservices/EShopOnAbp.Shared.Hosting.Microservices.csproj
  50. 59
      shared/EShopOnAbp.Shared.Hosting.Microservices/EShopOnAbpSharedHostingMicroservicesModule.cs
  51. 29
      shared/EShopOnAbp.Shared.Hosting.Microservices/JwtBearerConfigurationHelper.cs
  52. 39
      shared/EShopOnAbp.Shared.Localization/EShopOnAbpSharedLocalizationModule.cs

21
README.md

@ -30,7 +30,13 @@ You can either run in Visual Studio, or using [Microsoft Tye](https://github.com
- Wait until all applications are up!
- You can check running application from tye dashboard ([localhost:8000](http://127.0.0.1:8000/))
- You can check running application from tye dashboard ([localhost:8000](http://127.0.0.1:8000/))
- **Note**: If you see all of your applications keep restarting on tye dashboard or tye console, you may be facing ssl certificate issues. To diagnose the problems better, check any application logs. If it is related with SSL, developer certificate creation may have failed because of powershell issues regarding authorization. Check the powershell script running configuration and set policy for your local machine as:
```bash
Get-ExecutionPolicy list
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine
```
See [Microsoft Powershell documentation](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/get-executionpolicy?view=powershell-7.2) for more information.
- After all your backend services are up, start the angular application:
@ -39,6 +45,19 @@ You can either run in Visual Studio, or using [Microsoft Tye](https://github.com
yarn start
```
### Certificate Expiration
If the cerficiate is expired you'll see following error:
<!-- Make it smaller with 320px height -->
<img src="docs/images/ssl-error.png" height="320"/>
Generating a new certificate will fix that issue. To generate new one,
- Remove `etc/dev-cert/localhost.pfx`
- Manually execute `create-certificate.ps1` **or** re-run solution with `run-tye.ps1`
## Roadmap
- [x] New blank micro-service solution ✔️

BIN
docs/images/ssl-error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
docs/roadmap/Phase_1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 KiB

After

Width:  |  Height:  |  Size: 517 KiB

9
services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/AdministrationServiceHttpApiHostModule.cs

@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.AdministrationService.DbMigrations;
using EShopOnAbp.AdministrationService.EntityFrameworkCore;
using EShopOnAbp.Shared.Hosting.AspNetCore;
@ -11,12 +7,15 @@ using Microsoft.AspNetCore.Cors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy;
using Volo.Abp.Http.Client.IdentityModel.Web;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace EShopOnAbp.AdministrationService;

13
services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/Controllers/HomeController.cs

@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace EShopOnAbp.AdministrationService.Controllers
namespace EShopOnAbp.AdministrationService.Controllers;
public class HomeController : AbpController
{
public class HomeController : AbpController
public ActionResult Index()
{
public ActionResult Index()
{
return Redirect("~/swagger");
}
return Redirect("~/swagger");
}
}
}

33
services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/DbMigrations/AdministrationServiceDatabaseMigrationChecker.cs

@ -5,23 +5,22 @@ using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.AdministrationService.DbMigrations
namespace EShopOnAbp.AdministrationService.DbMigrations;
public class AdministrationServiceDatabaseMigrationChecker
: PendingEfCoreMigrationsChecker<AdministrationServiceDbContext>
{
public class AdministrationServiceDatabaseMigrationChecker : PendingEfCoreMigrationsChecker<AdministrationServiceDbContext>
public AdministrationServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
AdministrationServiceDbProperties.ConnectionStringName)
{
public AdministrationServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
AdministrationServiceDbProperties.ConnectionStringName)
{
}
}
}
}

133
services/administration/src/EShopOnAbp.AdministrationService.HttpApi.Host/DbMigrations/AdministrationServiceDatabaseMigrationEventHandler.cs

@ -1,101 +1,94 @@
using EShopOnAbp.AdministrationService.EntityFrameworkCore;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using Serilog;
using System;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow;
namespace EShopOnAbp.AdministrationService.DbMigrations
{
public class AdministrationServiceDatabaseMigrationEventHandler
namespace EShopOnAbp.AdministrationService.DbMigrations;
public class AdministrationServiceDatabaseMigrationEventHandler
: DatabaseEfCoreMigrationEventHandler<AdministrationServiceDbContext>,
IDistributedEventHandler<TenantCreatedEto>,
IDistributedEventHandler<ApplyDatabaseMigrationsEto>
IDistributedEventHandler<ApplyDatabaseMigrationsEto>
{
private readonly IPermissionDefinitionManager _permissionDefinitionManager;
private readonly IPermissionDataSeeder _permissionDataSeeder;
public AdministrationServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IPermissionDefinitionManager permissionDefinitionManager,
IPermissionDataSeeder permissionDataSeeder,
IDistributedEventBus distributedEventBus,
IAbpDistributedLock distributedLockProvider
) : base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
AdministrationServiceDbProperties.ConnectionStringName,
distributedLockProvider
)
{
private readonly IPermissionDefinitionManager _permissionDefinitionManager;
private readonly IPermissionDataSeeder _permissionDataSeeder;
_permissionDefinitionManager = permissionDefinitionManager;
_permissionDataSeeder = permissionDataSeeder;
}
public AdministrationServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IPermissionDefinitionManager permissionDefinitionManager,
IPermissionDataSeeder permissionDataSeeder,
IDistributedEventBus distributedEventBus
) : base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
AdministrationServiceDbProperties.ConnectionStringName)
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
{
if (eventData.DatabaseName != DatabaseName)
{
_permissionDefinitionManager = permissionDefinitionManager;
_permissionDataSeeder = permissionDataSeeder;
return;
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
try
{
if (eventData.DatabaseName != DatabaseName)
await using (var handle = await DistributedLockProvider.TryAcquireAsync(DatabaseName))
{
return;
}
Log.Information("AdministrationService acquired lock for db migration and seeding...");
try
{
var schemaMigrated = await MigrateDatabaseSchemaAsync(eventData.TenantId);
await SeedDataAsync(eventData.TenantId);
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
if (handle != null)
{
await MigrateDatabaseSchemaAsync();
await SeedDataAsync();
}
}
}
public async Task HandleEventAsync(TenantCreatedEto eventData)
catch (Exception ex)
{
try
{
await MigrateDatabaseSchemaAsync(eventData.Id);
await SeedDataAsync(eventData.Id);
}
catch (Exception ex)
{
await HandleErrorTenantCreatedAsync(eventData, ex);
}
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
}
}
private async Task SeedDataAsync(Guid? tenantId)
private async Task SeedDataAsync()
{
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
{
using (CurrentTenant.Change(tenantId))
{
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
{
var multiTenancySide = tenantId == null
? MultiTenancySides.Host
: MultiTenancySides.Tenant;
var multiTenancySide = MultiTenancySides.Host;
var permissionNames = _permissionDefinitionManager
.GetPermissions()
.Where(p => p.MultiTenancySide.HasFlag(multiTenancySide))
.Where(p => !p.Providers.Any() || p.Providers.Contains(RolePermissionValueProvider.ProviderName))
.Select(p => p.Name)
.ToArray();
var permissionNames = _permissionDefinitionManager
.GetPermissions()
.Where(p => p.MultiTenancySide.HasFlag(multiTenancySide))
.Where(p => !p.Providers.Any() ||
p.Providers.Contains(RolePermissionValueProvider.ProviderName))
.Select(p => p.Name)
.ToArray();
await _permissionDataSeeder.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
permissionNames,
tenantId
);
await _permissionDataSeeder.SeedAsync(
RolePermissionValueProvider.ProviderName,
"admin",
permissionNames
);
await uow.CompleteAsync();
}
}
await uow.CompleteAsync();
}
}
}
}

273
services/basket/src/EShopOnAbp.BasketService/BasketServiceModule.cs

@ -16,175 +16,174 @@ using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace EShopOnAbp.BasketService
namespace EShopOnAbp.BasketService;
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpHttpClientModule),
typeof(AbpAutoMapperModule),
typeof(AbpCachingModule),
typeof(AbpDddApplicationModule),
typeof(AbpDddDomainModule),
typeof(BasketServiceContractsModule),
typeof(EShopOnAbpSharedHostingMicroservicesModule)
)]
public class BasketServiceModule : AbpModule
{
[DependsOn(
typeof(AbpAspNetCoreMvcModule),
typeof(AbpHttpClientModule),
typeof(AbpAutoMapperModule),
typeof(AbpCachingModule),
typeof(AbpDddApplicationModule),
typeof(AbpDddDomainModule),
typeof(BasketServiceContractsModule),
typeof(EShopOnAbpSharedHostingMicroservicesModule)
)]
public class BasketServiceModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
var hostingEnvironment = context.Services.GetHostingEnvironment();
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = hostingEnvironment.IsDevelopment();
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = hostingEnvironment.IsDevelopment();
var configuration = context.Services.GetConfiguration();
var configuration = context.Services.GetConfiguration();
ConfigureAutoMapper();
ConfigureAspNetCoreRouting(context);
ConfigureGrpc(context);
ConfigureDistributedCache();
ConfigureVirtualFileSystem();
ConfigureAuthentication(context, configuration);
ConfigureSwagger(context, configuration);
ConfigureAutoApiControllers();
}
ConfigureAutoMapper();
ConfigureAspNetCoreRouting(context);
ConfigureGrpc(context);
ConfigureDistributedCache();
ConfigureVirtualFileSystem();
ConfigureAuthentication(context, configuration);
ConfigureSwagger(context, configuration);
ConfigureAutoApiControllers();
}
private void ConfigureAspNetCoreRouting(ServiceConfigurationContext context)
private void ConfigureAspNetCoreRouting(ServiceConfigurationContext context)
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
Configure<AbpAspNetCoreMvcOptions>(options =>
options.ConventionalControllers.Create(typeof(BasketServiceModule).Assembly, opts =>
{
options.ConventionalControllers.Create(typeof(BasketServiceModule).Assembly, opts =>
{
opts.RootPath = "basket";
opts.RemoteServiceName = BasketServiceConstants.RemoteServiceName;
});
opts.RootPath = "basket";
opts.RemoteServiceName = BasketServiceConstants.RemoteServiceName;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
});
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
app.UseCorrelationId();
app.UseCors();
app.UseAbpRequestLocalization();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAbpClaimsMap();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Basket Service API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
});
app.UseAbpSerilogEnrichers();
app.UseAuditing();
app.UseUnitOfWork();
app.UseConfiguredEndpoints();
}
private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
if (env.IsDevelopment())
{
SwaggerConfigurationHelper.ConfigureWithAuth(
context: context,
authority: configuration["AuthServer:Authority"],
scopes: new
Dictionary<string, string> /* Requested scopes for authorization code request and descriptions for swagger UI only */
{
{"BasketService", "Basket Service API"}
},
apiTitle: "Basket Service API"
);
app.UseDeveloperExceptionPage();
}
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
app.UseCorrelationId();
app.UseCors();
app.UseAbpRequestLocalization();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAbpClaimsMap();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
JwtBearerConfigurationHelper.Configure(context, "BasketService");
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Basket Service API");
options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
});
app.UseAbpSerilogEnrichers();
app.UseAuditing();
app.UseUnitOfWork();
app.UseConfiguredEndpoints();
}
private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
{
SwaggerConfigurationHelper.ConfigureWithAuth(
context: context,
authority: configuration["AuthServer:Authority"],
scopes: new
Dictionary<string, string> /* Requested scopes for authorization code request and descriptions for swagger UI only */
{
{"BasketService", "Basket Service API"}
},
apiTitle: "Basket Service API"
);
}
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
JwtBearerConfigurationHelper.Configure(context, "BasketService");
context.Services.AddCors(options =>
context.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
options.AddDefaultPolicy(builder =>
{
builder
.WithOrigins(
configuration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.Trim().RemovePostFix("/"))
.ToArray()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
builder
.WithOrigins(
configuration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.Trim().RemovePostFix("/"))
.ToArray()
)
.WithAbpExposedHeaders()
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
Configure<AbpAntiForgeryOptions>(options => { options.AutoValidate = false; });
}
Configure<AbpAntiForgeryOptions>(options => { options.AutoValidate = false; });
}
private void ConfigureAutoApiControllers()
private void ConfigureAutoApiControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers.Create(typeof(BasketServiceModule).Assembly);
});
}
options.ConventionalControllers.Create(typeof(BasketServiceModule).Assembly);
});
}
private void ConfigureGrpc(ServiceConfigurationContext context)
private void ConfigureGrpc(ServiceConfigurationContext context)
{
context.Services.AddGrpcClient<ProductPublic.ProductPublicClient>((services, options) =>
{
context.Services.AddGrpcClient<ProductPublic.ProductPublicClient>((services, options) =>
{
var remoteServiceOptions = services.GetRequiredService<IOptionsMonitor<AbpRemoteServiceOptions>>().CurrentValue;
var catalogServiceConfiguration = remoteServiceOptions.RemoteServices.GetConfigurationOrDefault("Catalog");
var catalogGrpcUrl = catalogServiceConfiguration.GetOrDefault("GrpcUrl");
var remoteServiceOptions = services.GetRequiredService<IOptionsMonitor<AbpRemoteServiceOptions>>().CurrentValue;
var catalogServiceConfiguration = remoteServiceOptions.RemoteServices.GetConfigurationOrDefault("Catalog");
var catalogGrpcUrl = catalogServiceConfiguration.GetOrDefault("GrpcUrl");
options.Address = new Uri(catalogGrpcUrl);
});
}
options.Address = new Uri(catalogGrpcUrl);
});
}
private void ConfigureAutoMapper()
private void ConfigureAutoMapper()
{
Configure<AbpAutoMapperOptions>(options =>
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<BasketServiceModule>();
});
}
options.AddMaps<BasketServiceModule>();
});
}
private void ConfigureDistributedCache()
private void ConfigureDistributedCache()
{
Configure<AbpDistributedCacheOptions>(options =>
{
Configure<AbpDistributedCacheOptions>(options =>
options.CacheConfigurators.Add(cacheName =>
{
options.CacheConfigurators.Add(cacheName =>
if (cacheName == CacheNameAttribute.GetCacheName(typeof(Basket)))
{
if (cacheName == CacheNameAttribute.GetCacheName(typeof(Basket)))
return new DistributedCacheEntryOptions
{
return new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(7)
};
}
SlidingExpiration = TimeSpan.FromDays(7)
};
}
return null;
});
return null;
});
}
});
}
private void ConfigureVirtualFileSystem()
private void ConfigureVirtualFileSystem()
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BasketServiceModule>();
});
}
options.FileSets.AddEmbedded<BasketServiceModule>();
});
}
}
}

9
services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/CatalogServiceHttpApiHostModule.cs

@ -1,24 +1,21 @@
using EShopOnAbp.CatalogService.DbMigrations;
using EShopOnAbp.CatalogService.Grpc;
using EShopOnAbp.CatalogService.MongoDB;
using EShopOnAbp.Shared.Hosting.AspNetCore;
using EShopOnAbp.Shared.Hosting.Microservices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.CatalogService.Grpc;
using EShopOnAbp.CatalogService.MongoDB;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace EShopOnAbp.CatalogService;

13
services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/Controllers/HomeController.cs

@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace EShopOnAbp.CatalogService.Controllers
namespace EShopOnAbp.CatalogService.Controllers;
public class HomeController : AbpController
{
public class HomeController : AbpController
public ActionResult Index()
{
public ActionResult Index()
{
return Redirect("~/swagger");
}
return Redirect("~/swagger");
}
}
}

49
services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/DbMigrations/CatalogServiceDatabaseMigrationChecker.cs

@ -6,32 +6,31 @@ using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.CatalogService.DbMigrations
namespace EShopOnAbp.CatalogService.DbMigrations;
public class CatalogServiceDatabaseMigrationChecker : PendingMongoDbMigrationsChecker<CatalogServiceMongoDbContext>
{
public class CatalogServiceDatabaseMigrationChecker : PendingMongoDbMigrationsChecker<CatalogServiceMongoDbContext>
{
private readonly ProductServiceDataSeeder _productServiceDataSeeder;
private readonly ProductServiceDataSeeder _productServiceDataSeeder;
public CatalogServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus,
ProductServiceDataSeeder productServiceDataSeeder)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
CatalogServiceDbProperties.ConnectionStringName)
{
_productServiceDataSeeder = productServiceDataSeeder;
}
public CatalogServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus,
ProductServiceDataSeeder productServiceDataSeeder)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
CatalogServiceDbProperties.ConnectionStringName)
{
_productServiceDataSeeder = productServiceDataSeeder;
}
public override async Task CheckAsync()
{
await base.CheckAsync();
await _productServiceDataSeeder.SeedAsync();
}
public override async Task CheckAsync()
{
await base.CheckAsync();
await _productServiceDataSeeder.SeedAsync();
}
}
}

83
services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/DbMigrations/CatalogServiceDatabaseMigrationEventHandler.cs

@ -1,53 +1,66 @@
using System;
using System.Threading.Tasks;
using EShopOnAbp.CatalogService.MongoDB;
using EShopOnAbp.CatalogService.MongoDB;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.MongoDb;
using System;
using System.Threading.Tasks;
using Serilog;
using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.CatalogService.DbMigrations
{
public class CatalogServiceDatabaseMigrationEventHandler
: DatabaseMongoDbMigrationEventHandler<CatalogServiceMongoDbContext>,
namespace EShopOnAbp.CatalogService.DbMigrations;
public class CatalogServiceDatabaseMigrationEventHandler
: DatabaseMongoDbMigrationEventHandler<CatalogServiceMongoDbContext>,
IDistributedEventHandler<ApplyDatabaseMigrationsEto>
{
public CatalogServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus,
IServiceProvider serviceProvider,
IAbpDistributedLock distributedLockProvider
) : base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
CatalogServiceDbProperties.ConnectionStringName,
serviceProvider,
distributedLockProvider)
{
public CatalogServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus,
IServiceProvider serviceProvider
) : base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
CatalogServiceDbProperties.ConnectionStringName,serviceProvider)
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
{
if (eventData.DatabaseName != DatabaseName)
{
return;
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
if (eventData.TenantId != null)
{
if (eventData.DatabaseName != DatabaseName)
{
return;
}
return;
}
if (eventData.TenantId != null)
{
return;
}
try
{
Log.Information("CatalogService has acquired lock for db migration...");
try
await using (var handle = await DistributedLockProvider.TryAcquireAsync(DatabaseName))
{
await MigrateDatabaseSchemaAsync(null);
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
if (handle != null)
{
Log.Information("CatalogService is migrating database...");
await MigrateDatabaseSchemaAsync();
}
}
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
}
}
}
}

172
services/catalog/src/EShopOnAbp.CatalogService.HttpApi.Host/DbMigrations/ProductServiceDataSeeder.cs

@ -1,110 +1,108 @@
using System;
using System.Threading.Tasks;
using EShopOnAbp.CatalogService.Products;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;
namespace EShopOnAbp.CatalogService.DbMigrations
namespace EShopOnAbp.CatalogService.DbMigrations;
public class ProductServiceDataSeeder : ITransientDependency
{
public class ProductServiceDataSeeder : ITransientDependency
private readonly ProductManager _productManager;
private readonly IRepository<Product, Guid> _productRepository;
public ProductServiceDataSeeder(
IRepository<Product, Guid> productRepository,
ProductManager productManager)
{
private readonly ProductManager _productManager;
private readonly IRepository<Product, Guid> _productRepository;
_productRepository = productRepository;
_productManager = productManager;
}
public ProductServiceDataSeeder(
IRepository<Product, Guid> productRepository,
ProductManager productManager)
{
_productRepository = productRepository;
_productManager = productManager;
}
[UnitOfWork]
public virtual async Task SeedAsync()
{
await AddProductsAsync();
}
[UnitOfWork]
public virtual async Task SeedAsync()
private async Task AddProductsAsync()
{
if (await _productRepository.GetCountAsync() > 0)
{
await AddProductsAsync();
return;
}
private async Task AddProductsAsync()
{
if (await _productRepository.GetCountAsync() > 0)
{
return;
}
await _productManager.CreateAsync(
"ABP04918",
"Lego Star Wars - 75059 Sandcrawler UCS",
999,
42,
"lego.jpg"
);
await _productManager.CreateAsync(
"ABP04918",
"Lego Star Wars - 75059 Sandcrawler UCS",
999,
42,
"lego.jpg"
);
await _productManager.CreateAsync(
"ABP23849",
"Nikon AF-S 50mm f/1.8 G Lens",
1499,
56,
"nikon.jpg"
);
await _productManager.CreateAsync(
"ABP23849",
"Nikon AF-S 50mm f/1.8 G Lens",
1499,
56,
"nikon.jpg"
);
await _productManager.CreateAsync(
"ABP82731",
"Beats Solo3 Wireless On-Ear Headphone",
97,
20,
"beats.jpg"
);
await _productManager.CreateAsync(
"ABP82731",
"Beats Solo3 Wireless On-Ear Headphone",
97,
20,
"beats.jpg"
);
await _productManager.CreateAsync(
"ABP12322",
"Rampage Sn-Rw2 Gamer Headphone",
654,
42,
"rampage.jpg"
);
await _productManager.CreateAsync(
"ABP12322",
"Rampage Sn-Rw2 Gamer Headphone",
654,
42,
"rampage.jpg"
);
await _productManager.CreateAsync(
"ABP00291",
"Asus Transformer Book T300CHI-FH011H",
1249,
3,
"asus.jpg"
);
await _productManager.CreateAsync(
"ABP00291",
"Asus Transformer Book T300CHI-FH011H",
1249,
3,
"asus.jpg"
);
await _productManager.CreateAsync(
"ABP02918",
"OKI C332DN Dublex + Network A4 Laser Printer",
215,
6,
"oki.jpg"
);
await _productManager.CreateAsync(
"ABP02918",
"OKI C332DN Dublex + Network A4 Laser Printer",
215,
6,
"oki.jpg"
);
await _productManager.CreateAsync(
"ABP11121",
"Bluecat Rd810 Mini Led",
449,
13,
"bluecat.jpg"
);
await _productManager.CreateAsync(
"ABP11121",
"Bluecat Rd810 Mini Led",
449,
13,
"bluecat.jpg"
);
await _productManager.CreateAsync(
"ABP44432",
"Sunny 55\" TV 4K Ultra HD Curved Smart Led TV",
2249,
1,
"sunny.jpg"
);
await _productManager.CreateAsync(
"ABP44432",
"Sunny 55\" TV 4K Ultra HD Curved Smart Led TV",
2249,
1,
"sunny.jpg"
);
await _productManager.CreateAsync(
"ABP37182",
"Sony Playstation 4 Slim 500 GB (PAL)",
699,
120,
"playstation.jpg"
);
}
await _productManager.CreateAsync(
"ABP37182",
"Sony Playstation 4 Slim 500 GB (PAL)",
699,
120,
"playstation.jpg"
);
}
}
}

2
services/catalog/src/EShopOnAbp.CatalogService.MongoDB/MongoDB/CatalogServiceMongoDbContext.cs

@ -8,7 +8,7 @@ namespace EShopOnAbp.CatalogService.MongoDB
[ConnectionStringName(CatalogServiceDbProperties.ConnectionStringName)]
public class CatalogServiceMongoDbContext : AbpMongoDbContext, ICatalogServiceMongoDbContext
{
public IMongoCollection<Product> Questions => Collection<Product>();
public IMongoCollection<Product> Products => Collection<Product>();
protected override void CreateModel(IMongoModelBuilder modelBuilder)
{

13
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/Controllers/HomeController.cs

@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace EShopOnAbp.IdentityService.Controllers
namespace EShopOnAbp.IdentityService.Controllers;
public class HomeController : AbpController
{
public class HomeController : AbpController
public ActionResult Index()
{
public ActionResult Index()
{
return Redirect("~/swagger");
}
return Redirect("~/swagger");
}
}
}

11
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/ApplyDatabaseSeedsEto.cs

@ -1,10 +1,9 @@
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus;
namespace EShopOnAbp.IdentityService.DbMigrations
namespace EShopOnAbp.IdentityService.DbMigrations;
[EventName("abp.identity.apply_database_seeds")]
public class ApplyDatabaseSeedsEto : EtoBase
{
[EventName("abp.identity.apply_database_seeds")]
public class ApplyDatabaseSeedsEto : EtoBase
{
}
}
}

28
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/DataSeederEventHandler.cs

@ -1,23 +1,21 @@
using System;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
namespace EShopOnAbp.IdentityService.DbMigrations
namespace EShopOnAbp.IdentityService.DbMigrations;
public class DataSeederEventHandler : ILocalEventHandler<ApplyDatabaseSeedsEto>, ITransientDependency
{
public class DataSeederEventHandler : ILocalEventHandler<ApplyDatabaseSeedsEto>, ITransientDependency
{
protected IDataSeeder DataSeeder { get; }
protected IDataSeeder DataSeeder { get; }
public DataSeederEventHandler(IDataSeeder dataSeeder)
{
DataSeeder = dataSeeder;
}
public DataSeederEventHandler(IDataSeeder dataSeeder)
{
DataSeeder = dataSeeder;
}
public async Task HandleEventAsync(ApplyDatabaseSeedsEto eventData)
{
await DataSeeder.SeedAsync();
}
public async Task HandleEventAsync(ApplyDatabaseSeedsEto eventData)
{
await DataSeeder.SeedAsync();
}
}
}

626
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServerDataSeeder.cs

@ -20,381 +20,381 @@ using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource;
using ApiScope = Volo.Abp.IdentityServer.ApiScopes.ApiScope;
using Client = Volo.Abp.IdentityServer.Clients.Client;
namespace EShopOnAbp.IdentityService.DbMigrations
namespace EShopOnAbp.IdentityService.DbMigrations;
public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependency
{
public class IdentityServerDataSeeder : IDataSeedContributor, ITransientDependency
private readonly IApiResourceRepository _apiResourceRepository;
private readonly IApiScopeRepository _apiScopeRepository;
private readonly IClientRepository _clientRepository;
private readonly IIdentityResourceDataSeeder _identityResourceDataSeeder;
private readonly IGuidGenerator _guidGenerator;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IConfiguration _configuration;
private readonly ICurrentTenant _currentTenant;
public IdentityServerDataSeeder(
IClientRepository clientRepository,
IApiResourceRepository apiResourceRepository,
IApiScopeRepository apiScopeRepository,
IIdentityResourceDataSeeder identityResourceDataSeeder,
IGuidGenerator guidGenerator,
IPermissionDataSeeder permissionDataSeeder,
IConfiguration configuration,
ICurrentTenant currentTenant)
{
private readonly IApiResourceRepository _apiResourceRepository;
private readonly IApiScopeRepository _apiScopeRepository;
private readonly IClientRepository _clientRepository;
private readonly IIdentityResourceDataSeeder _identityResourceDataSeeder;
private readonly IGuidGenerator _guidGenerator;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IConfiguration _configuration;
private readonly ICurrentTenant _currentTenant;
_clientRepository = clientRepository;
_apiResourceRepository = apiResourceRepository;
_apiScopeRepository = apiScopeRepository;
_identityResourceDataSeeder = identityResourceDataSeeder;
_guidGenerator = guidGenerator;
_permissionDataSeeder = permissionDataSeeder;
_configuration = configuration;
_currentTenant = currentTenant;
}
public IdentityServerDataSeeder(
IClientRepository clientRepository,
IApiResourceRepository apiResourceRepository,
IApiScopeRepository apiScopeRepository,
IIdentityResourceDataSeeder identityResourceDataSeeder,
IGuidGenerator guidGenerator,
IPermissionDataSeeder permissionDataSeeder,
IConfiguration configuration,
ICurrentTenant currentTenant)
{
_clientRepository = clientRepository;
_apiResourceRepository = apiResourceRepository;
_apiScopeRepository = apiScopeRepository;
_identityResourceDataSeeder = identityResourceDataSeeder;
_guidGenerator = guidGenerator;
_permissionDataSeeder = permissionDataSeeder;
_configuration = configuration;
_currentTenant = currentTenant;
}
public virtual Task SeedAsync(DataSeedContext context)
{
return SeedAsync();
}
public virtual Task SeedAsync(DataSeedContext context)
[UnitOfWork]
public virtual async Task SeedAsync()
{
using (_currentTenant.Change(null))
{
return SeedAsync();
await _identityResourceDataSeeder.CreateStandardResourcesAsync();
await CreateApiResourcesAsync();
await CreateApiScopesAsync();
await CreateSwaggerClientsAsync();
await CreateClientsAsync();
}
}
[UnitOfWork]
public virtual async Task SeedAsync()
private async Task CreateApiResourcesAsync()
{
var commonApiUserClaims = new[]
{
using (_currentTenant.Change(null))
{
await _identityResourceDataSeeder.CreateStandardResourcesAsync();
await CreateApiResourcesAsync();
await CreateApiScopesAsync();
await CreateSwaggerClientsAsync();
await CreateClientsAsync();
}
}
"email",
"email_verified",
"name",
"phone_number",
"phone_number_verified",
"role"
};
private async Task CreateApiResourcesAsync()
{
var commonApiUserClaims = new[]
{
"email",
"email_verified",
"name",
"phone_number",
"phone_number_verified",
"role"
};
await CreateApiResourceAsync("AccountService", commonApiUserClaims);
await CreateApiResourceAsync("IdentityService", commonApiUserClaims);
await CreateApiResourceAsync("AdministrationService", commonApiUserClaims);
await CreateApiResourceAsync("CatalogService", commonApiUserClaims);
await CreateApiResourceAsync("BasketService", commonApiUserClaims);
await CreateApiResourceAsync("OrderingService", commonApiUserClaims);
await CreateApiResourceAsync("PaymentService", commonApiUserClaims);
}
await CreateApiResourceAsync("AccountService", commonApiUserClaims);
await CreateApiResourceAsync("IdentityService", commonApiUserClaims);
await CreateApiResourceAsync("AdministrationService", commonApiUserClaims);
await CreateApiResourceAsync("CatalogService", commonApiUserClaims);
await CreateApiResourceAsync("BasketService", commonApiUserClaims);
await CreateApiResourceAsync("OrderingService", commonApiUserClaims);
await CreateApiResourceAsync("PaymentService", commonApiUserClaims);
}
private async Task CreateApiScopesAsync()
{
await CreateApiScopeAsync("AccountService");
await CreateApiScopeAsync("IdentityService");
await CreateApiScopeAsync("AdministrationService");
await CreateApiScopeAsync("CatalogService");
await CreateApiScopeAsync("BasketService");
await CreateApiScopeAsync("OrderingService");
await CreateApiScopeAsync("PaymentService");
}
private async Task CreateSwaggerClientsAsync()
{
await CreateWebGatewaySwaggerClientAsync("WebGateway",
new[]
{
"AccountService", "IdentityService", "AdministrationService",
"CatalogService", "BasketService",
"PaymentService", "OrderingService"
});
}
private async Task CreateApiScopesAsync()
private async Task CreateWebGatewaySwaggerClientAsync(string name, string[] scopes = null)
{
var commonScopes = new[]
{
await CreateApiScopeAsync("AccountService");
await CreateApiScopeAsync("IdentityService");
await CreateApiScopeAsync("AdministrationService");
await CreateApiScopeAsync("CatalogService");
await CreateApiScopeAsync("BasketService");
await CreateApiScopeAsync("OrderingService");
await CreateApiScopeAsync("PaymentService");
}
"email",
"openid",
"profile",
"role",
"phone",
"address"
};
scopes ??= new[] {name};
private async Task CreateSwaggerClientsAsync()
// Swagger Client
var swaggerClientId = $"{name}_Swagger";
if (!swaggerClientId.IsNullOrWhiteSpace())
{
await CreateWebGatewaySwaggerClientAsync("WebGateway",
new[]
var webGatewaySwaggerRootUrl = _configuration[$"IdentityServerClients:{name}:RootUrl"].TrimEnd('/');
var publicWebGatewayRootUrl = _configuration[$"IdentityServerClients:PublicWebGateway:RootUrl"].TrimEnd('/');
var accountServiceRootUrl = _configuration[$"IdentityServerClients:AccountService:RootUrl"].TrimEnd('/');
var identityServiceRootUrl = _configuration[$"IdentityServerClients:IdentityService:RootUrl"].TrimEnd('/');
var administrationServiceRootUrl = _configuration[$"IdentityServerClients:AdministrationService:RootUrl"].TrimEnd('/');
var catalogServiceRootUrl = _configuration[$"IdentityServerClients:CatalogService:RootUrl"].TrimEnd('/');
var basketServiceRootUrl = _configuration[$"IdentityServerClients:BasketService:RootUrl"].TrimEnd('/');
var orderingServiceRootUrl = _configuration[$"IdentityServerClients:OrderingService:RootUrl"].TrimEnd('/');
var paymentServiceRootUrl = _configuration[$"IdentityServerClients:PaymentService:RootUrl"].TrimEnd('/');
await CreateClientAsync(
name: swaggerClientId,
scopes: commonScopes.Union(scopes),
grantTypes: new[] {"authorization_code"},
secret: "1q2w3e*".Sha256(),
requireClientSecret: false,
redirectUris: new List<string>
{
$"{webGatewaySwaggerRootUrl}/swagger/oauth2-redirect.html", // WebGateway redirect uri
$"{publicWebGatewayRootUrl}/swagger/oauth2-redirect.html", // PublicWebGateway redirect uri
$"{accountServiceRootUrl}/swagger/oauth2-redirect.html", // AccountService redirect uri
$"{identityServiceRootUrl}/swagger/oauth2-redirect.html", // IdentityService redirect uri
$"{administrationServiceRootUrl}/swagger/oauth2-redirect.html", // AdministrationService redirect uri
$"{catalogServiceRootUrl}/swagger/oauth2-redirect.html", // CatalogService redirect uri
$"{basketServiceRootUrl}/swagger/oauth2-redirect.html", // BasketService redirect uri
$"{orderingServiceRootUrl}/swagger/oauth2-redirect.html", // OrderingService redirect uri
$"{paymentServiceRootUrl}/swagger/oauth2-redirect.html" // PaymentService redirect uri
},
corsOrigins: new[]
{
"AccountService", "IdentityService", "AdministrationService",
"CatalogService", "BasketService",
"PaymentService", "OrderingService"
});
webGatewaySwaggerRootUrl.RemovePostFix("/"),
publicWebGatewayRootUrl.RemovePostFix("/"),
accountServiceRootUrl.RemovePostFix("/"),
identityServiceRootUrl.RemovePostFix("/"),
administrationServiceRootUrl.RemovePostFix("/"),
catalogServiceRootUrl.RemovePostFix("/"),
basketServiceRootUrl.RemovePostFix("/"),
orderingServiceRootUrl.RemovePostFix("/"),
paymentServiceRootUrl.RemovePostFix("/")
}
);
}
}
private async Task CreateWebGatewaySwaggerClientAsync(string name, string[] scopes = null)
private async Task<ApiResource> CreateApiResourceAsync(string name, IEnumerable<string> claims)
{
var apiResource = await _apiResourceRepository.FindByNameAsync(name);
if (apiResource == null)
{
var commonScopes = new[]
{
"email",
"openid",
"profile",
"role",
"phone",
"address"
};
scopes ??= new[] {name};
// Swagger Client
var swaggerClientId = $"{name}_Swagger";
if (!swaggerClientId.IsNullOrWhiteSpace())
{
var webGatewaySwaggerRootUrl = _configuration[$"IdentityServerClients:{name}:RootUrl"].TrimEnd('/');
var publicWebGatewayRootUrl = _configuration[$"IdentityServerClients:PublicWebGateway:RootUrl"].TrimEnd('/');
var accountServiceRootUrl = _configuration[$"IdentityServerClients:AccountService:RootUrl"].TrimEnd('/');
var identityServiceRootUrl = _configuration[$"IdentityServerClients:IdentityService:RootUrl"].TrimEnd('/');
var administrationServiceRootUrl = _configuration[$"IdentityServerClients:AdministrationService:RootUrl"].TrimEnd('/');
var catalogServiceRootUrl = _configuration[$"IdentityServerClients:CatalogService:RootUrl"].TrimEnd('/');
var basketServiceRootUrl = _configuration[$"IdentityServerClients:BasketService:RootUrl"].TrimEnd('/');
var orderingServiceRootUrl = _configuration[$"IdentityServerClients:OrderingService:RootUrl"].TrimEnd('/');
var paymentServiceRootUrl = _configuration[$"IdentityServerClients:PaymentService:RootUrl"].TrimEnd('/');
await CreateClientAsync(
name: swaggerClientId,
scopes: commonScopes.Union(scopes),
grantTypes: new[] {"authorization_code"},
secret: "1q2w3e*".Sha256(),
requireClientSecret: false,
redirectUris: new List<string>
{
$"{webGatewaySwaggerRootUrl}/swagger/oauth2-redirect.html", // WebGateway redirect uri
$"{publicWebGatewayRootUrl}/swagger/oauth2-redirect.html", // PublicWebGateway redirect uri
$"{accountServiceRootUrl}/swagger/oauth2-redirect.html", // AccountService redirect uri
$"{identityServiceRootUrl}/swagger/oauth2-redirect.html", // IdentityService redirect uri
$"{administrationServiceRootUrl}/swagger/oauth2-redirect.html", // AdministrationService redirect uri
$"{catalogServiceRootUrl}/swagger/oauth2-redirect.html", // CatalogService redirect uri
$"{basketServiceRootUrl}/swagger/oauth2-redirect.html", // BasketService redirect uri
$"{orderingServiceRootUrl}/swagger/oauth2-redirect.html", // OrderingService redirect uri
$"{paymentServiceRootUrl}/swagger/oauth2-redirect.html" // PaymentService redirect uri
},
corsOrigins: new[]
{
webGatewaySwaggerRootUrl.RemovePostFix("/"),
publicWebGatewayRootUrl.RemovePostFix("/"),
accountServiceRootUrl.RemovePostFix("/"),
identityServiceRootUrl.RemovePostFix("/"),
administrationServiceRootUrl.RemovePostFix("/"),
catalogServiceRootUrl.RemovePostFix("/"),
basketServiceRootUrl.RemovePostFix("/"),
orderingServiceRootUrl.RemovePostFix("/"),
paymentServiceRootUrl.RemovePostFix("/")
}
);
}
apiResource = await _apiResourceRepository.InsertAsync(
new ApiResource(
_guidGenerator.Create(),
name,
name + " API"
),
autoSave: true
);
}
private async Task<ApiResource> CreateApiResourceAsync(string name, IEnumerable<string> claims)
foreach (var claim in claims)
{
var apiResource = await _apiResourceRepository.FindByNameAsync(name);
if (apiResource == null)
if (apiResource.FindClaim(claim) == null)
{
apiResource = await _apiResourceRepository.InsertAsync(
new ApiResource(
_guidGenerator.Create(),
name,
name + " API"
),
autoSave: true
);
}
foreach (var claim in claims)
{
if (apiResource.FindClaim(claim) == null)
{
apiResource.AddUserClaim(claim);
}
apiResource.AddUserClaim(claim);
}
return await _apiResourceRepository.UpdateAsync(apiResource);
}
private async Task<ApiScope> CreateApiScopeAsync(string name)
{
var apiScope = await _apiScopeRepository.FindByNameAsync(name);
if (apiScope == null)
{
apiScope = await _apiScopeRepository.InsertAsync(
new ApiScope(
_guidGenerator.Create(),
name,
name + " API"
),
autoSave: true
);
}
return await _apiResourceRepository.UpdateAsync(apiResource);
}
return apiScope;
private async Task<ApiScope> CreateApiScopeAsync(string name)
{
var apiScope = await _apiScopeRepository.FindByNameAsync(name);
if (apiScope == null)
{
apiScope = await _apiScopeRepository.InsertAsync(
new ApiScope(
_guidGenerator.Create(),
name,
name + " API"
),
autoSave: true
);
}
private async Task CreateClientsAsync()
return apiScope;
}
private async Task CreateClientsAsync()
{
var commonScopes = new[]
{
var commonScopes = new[]
"email",
"openid",
"profile",
"role",
"phone",
"address"
};
//Public Web Client
var publicWebClientRootUrl = _configuration["IdentityServerClients:PublicWeb:RootUrl"]
.EnsureEndsWith('/');
await CreateClientAsync(
name: "PublicWeb",
scopes: commonScopes.Union(new[]
{
"email",
"openid",
"profile",
"role",
"phone",
"address"
};
"AccountService",
"AdministrationService",
"CatalogService",
"BasketService",
"PaymentService",
"OrderingService"
}),
grantTypes: new[] {"hybrid"},
secret: "1q2w3e*".Sha256(),
redirectUris: new List<string>{ $"{publicWebClientRootUrl}signin-oidc" },
postLogoutRedirectUri: $"{publicWebClientRootUrl}signout-callback-oidc",
frontChannelLogoutUri: $"{publicWebClientRootUrl}Account/FrontChannelLogout",
corsOrigins: new[] {publicWebClientRootUrl.RemovePostFix("/")}
);
//Public Web Client
var publicWebClientRootUrl = _configuration["IdentityServerClients:PublicWeb:RootUrl"]
.EnsureEndsWith('/');
await CreateClientAsync(
name: "PublicWeb",
scopes: commonScopes.Union(new[]
{
"AccountService",
"AdministrationService",
"CatalogService",
"BasketService",
"PaymentService",
"OrderingService"
}),
grantTypes: new[] {"hybrid"},
secret: "1q2w3e*".Sha256(),
redirectUris: new List<string>{ $"{publicWebClientRootUrl}signin-oidc" },
postLogoutRedirectUri: $"{publicWebClientRootUrl}signout-callback-oidc",
frontChannelLogoutUri: $"{publicWebClientRootUrl}Account/FrontChannelLogout",
corsOrigins: new[] {publicWebClientRootUrl.RemovePostFix("/")}
);
//Angular Client
var angularClientRootUrl =
_configuration["IdentityServerClients:Web:RootUrl"].TrimEnd('/');
await CreateClientAsync(
name: "Web",
scopes: commonScopes.Union(new[]
{
"AccountService",
"IdentityService",
"AdministrationService",
"CatalogService",
"OrderingService"
}),
grantTypes: new[] {"authorization_code", "LinkLogin", "password"},
secret: "1q2w3e*".Sha256(),
requirePkce: true,
requireClientSecret: false,
redirectUris: new List<string>{ $"{angularClientRootUrl}" },
postLogoutRedirectUri: $"{angularClientRootUrl}",
corsOrigins: new[] {angularClientRootUrl}
);
//Angular Client
var angularClientRootUrl =
_configuration["IdentityServerClients:Web:RootUrl"].TrimEnd('/');
await CreateClientAsync(
name: "Web",
scopes: commonScopes.Union(new[]
{
"AccountService",
"IdentityService",
"AdministrationService",
"CatalogService"
}),
grantTypes: new[] {"authorization_code", "LinkLogin", "password"},
secret: "1q2w3e*".Sha256(),
requirePkce: true,
requireClientSecret: false,
redirectUris: new List<string>{ $"{angularClientRootUrl}" },
postLogoutRedirectUri: $"{angularClientRootUrl}",
corsOrigins: new[] {angularClientRootUrl}
);
//Administration Service Client
await CreateClientAsync(
name: "EShopOnAbp_AdministrationService",
scopes: commonScopes.Union(new[]
{
"IdentityService"
}),
grantTypes: new[] {"client_credentials"},
secret: "1q2w3e*".Sha256(),
permissions: new[] {IdentityPermissions.Users.Default}
);
}
//Administration Service Client
await CreateClientAsync(
name: "EShopOnAbp_AdministrationService",
scopes: commonScopes.Union(new[]
private async Task<Client> CreateClientAsync(
string name,
IEnumerable<string> scopes,
IEnumerable<string> grantTypes,
string secret = null,
List<string> redirectUris = null,
string postLogoutRedirectUri = null,
string frontChannelLogoutUri = null,
bool requireClientSecret = true,
bool requirePkce = false,
IEnumerable<string> permissions = null,
IEnumerable<string> corsOrigins = null)
{
var client = await _clientRepository.FindByClientIdAsync(name);
if (client == null)
{
client = await _clientRepository.InsertAsync(
new Client(
_guidGenerator.Create(),
name
)
{
"IdentityService"
}),
grantTypes: new[] {"client_credentials"},
secret: "1q2w3e*".Sha256(),
permissions: new[] {IdentityPermissions.Users.Default}
ClientName = name,
ProtocolType = "oidc",
Description = name,
AlwaysIncludeUserClaimsInIdToken = true,
AllowOfflineAccess = true,
AbsoluteRefreshTokenLifetime = 31536000, //365 days
AccessTokenLifetime = 31536000, //365 days
AuthorizationCodeLifetime = 300,
IdentityTokenLifetime = 300,
RequireConsent = false,
FrontChannelLogoutUri = frontChannelLogoutUri,
RequireClientSecret = requireClientSecret,
RequirePkce = requirePkce
},
autoSave: true
);
}
private async Task<Client> CreateClientAsync(
string name,
IEnumerable<string> scopes,
IEnumerable<string> grantTypes,
string secret = null,
List<string> redirectUris = null,
string postLogoutRedirectUri = null,
string frontChannelLogoutUri = null,
bool requireClientSecret = true,
bool requirePkce = false,
IEnumerable<string> permissions = null,
IEnumerable<string> corsOrigins = null)
foreach (var scope in scopes)
{
var client = await _clientRepository.FindByClientIdAsync(name);
if (client == null)
if (client.FindScope(scope) == null)
{
client = await _clientRepository.InsertAsync(
new Client(
_guidGenerator.Create(),
name
)
{
ClientName = name,
ProtocolType = "oidc",
Description = name,
AlwaysIncludeUserClaimsInIdToken = true,
AllowOfflineAccess = true,
AbsoluteRefreshTokenLifetime = 31536000, //365 days
AccessTokenLifetime = 31536000, //365 days
AuthorizationCodeLifetime = 300,
IdentityTokenLifetime = 300,
RequireConsent = false,
FrontChannelLogoutUri = frontChannelLogoutUri,
RequireClientSecret = requireClientSecret,
RequirePkce = requirePkce
},
autoSave: true
);
}
foreach (var scope in scopes)
{
if (client.FindScope(scope) == null)
{
client.AddScope(scope);
}
client.AddScope(scope);
}
}
foreach (var grantType in grantTypes)
foreach (var grantType in grantTypes)
{
if (client.FindGrantType(grantType) == null)
{
if (client.FindGrantType(grantType) == null)
{
client.AddGrantType(grantType);
}
client.AddGrantType(grantType);
}
}
if (!secret.IsNullOrEmpty())
if (!secret.IsNullOrEmpty())
{
if (client.FindSecret(secret) == null)
{
if (client.FindSecret(secret) == null)
{
client.AddSecret(secret);
}
client.AddSecret(secret);
}
}
if (redirectUris != null)
if (redirectUris != null)
{
foreach (var redirectUri in redirectUris)
{
foreach (var redirectUri in redirectUris)
if (redirectUri != null)
{
if (redirectUri != null)
if (client.FindRedirectUri(redirectUri) == null)
{
if (client.FindRedirectUri(redirectUri) == null)
{
client.AddRedirectUri(redirectUri);
}
client.AddRedirectUri(redirectUri);
}
}
}
}
if (postLogoutRedirectUri != null)
if (postLogoutRedirectUri != null)
{
if (client.FindPostLogoutRedirectUri(postLogoutRedirectUri) == null)
{
if (client.FindPostLogoutRedirectUri(postLogoutRedirectUri) == null)
{
client.AddPostLogoutRedirectUri(postLogoutRedirectUri);
}
client.AddPostLogoutRedirectUri(postLogoutRedirectUri);
}
}
if (permissions != null)
{
await _permissionDataSeeder.SeedAsync(
ClientPermissionValueProvider.ProviderName,
name,
permissions,
null
);
}
if (permissions != null)
{
await _permissionDataSeeder.SeedAsync(
ClientPermissionValueProvider.ProviderName,
name,
permissions,
null
);
}
if (corsOrigins != null)
if (corsOrigins != null)
{
foreach (var origin in corsOrigins)
{
foreach (var origin in corsOrigins)
if (!origin.IsNullOrWhiteSpace() && client.FindCorsOrigin(origin) == null)
{
if (!origin.IsNullOrWhiteSpace() && client.FindCorsOrigin(origin) == null)
{
client.AddCorsOrigin(origin);
}
client.AddCorsOrigin(origin);
}
}
return await _clientRepository.UpdateAsync(client);
}
return await _clientRepository.UpdateAsync(client);
}
}

53
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServiceDatabaseMigrationChecker.cs

@ -7,38 +7,37 @@ using Volo.Abp.EventBus.Local;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.IdentityService.DbMigrations
namespace EShopOnAbp.IdentityService.DbMigrations;
public class IdentityServiceDatabaseMigrationChecker : PendingEfCoreMigrationsChecker<IdentityServiceDbContext>
{
public class IdentityServiceDatabaseMigrationChecker : PendingEfCoreMigrationsChecker<IdentityServiceDbContext>
protected ILocalEventBus LocalEventBus { get; }
public IdentityServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus,
ILocalEventBus localEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
IdentityServiceDbProperties.ConnectionStringName)
{
protected ILocalEventBus LocalEventBus { get; }
LocalEventBus = localEventBus;
}
public IdentityServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus,
ILocalEventBus localEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
IdentityServiceDbProperties.ConnectionStringName)
{
LocalEventBus = localEventBus;
}
public override async Task<bool> CheckAsync()
{
var isMigrationRequired = await base.CheckAsync();
public override async Task<bool> CheckAsync()
if (!isMigrationRequired)
{
var isMigrationRequired = await base.CheckAsync();
if (!isMigrationRequired)
{
await LocalEventBus.PublishAsync(new ApplyDatabaseSeedsEto());
}
return isMigrationRequired;
await LocalEventBus.PublishAsync(new ApplyDatabaseSeedsEto());
}
return isMigrationRequired;
}
}

142
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/DbMigrations/IdentityServiceDatabaseMigrationEventHandler.cs

@ -1,104 +1,90 @@
using EShopOnAbp.IdentityService.EntityFrameworkCore;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using Serilog;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.IdentityService.DbMigrations
namespace EShopOnAbp.IdentityService.DbMigrations;
public class IdentityServiceDatabaseMigrationEventHandler
: DatabaseEfCoreMigrationEventHandler<IdentityServiceDbContext>,
IDistributedEventHandler<ApplyDatabaseMigrationsEto>
{
public class IdentityServiceDatabaseMigrationEventHandler
: DatabaseEfCoreMigrationEventHandler<IdentityServiceDbContext>,
IDistributedEventHandler<TenantCreatedEto>,
IDistributedEventHandler<ApplyDatabaseMigrationsEto>
private readonly IIdentityDataSeeder _identityDataSeeder;
private readonly IdentityServerDataSeeder _identityServerDataSeeder;
private readonly ILocalEventBus _localEventBus;
public IdentityServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IIdentityDataSeeder identityDataSeeder,
IdentityServerDataSeeder identityServerDataSeeder,
IDistributedEventBus distributedEventBus,
ILocalEventBus localEventBus,
IAbpDistributedLock distributedLockProvider
) : base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
IdentityServiceDbProperties.ConnectionStringName,
distributedLockProvider)
{
private readonly IIdentityDataSeeder _identityDataSeeder;
private readonly IdentityServerDataSeeder _identityServerDataSeeder;
private readonly ILocalEventBus _localEventBus;
_identityDataSeeder = identityDataSeeder;
_identityServerDataSeeder = identityServerDataSeeder;
_localEventBus = localEventBus;
}
public IdentityServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IIdentityDataSeeder identityDataSeeder,
IdentityServerDataSeeder identityServerDataSeeder,
IDistributedEventBus distributedEventBus,
ILocalEventBus localEventBus
) : base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
IdentityServiceDbProperties.ConnectionStringName)
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
{
if (eventData.DatabaseName != DatabaseName)
{
_identityDataSeeder = identityDataSeeder;
_identityServerDataSeeder = identityServerDataSeeder;
_localEventBus = localEventBus;
return;
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
try
{
if (eventData.DatabaseName != DatabaseName)
{
return;
}
try
await using (var handle = await DistributedLockProvider.TryAcquireAsync(DatabaseName))
{
var schemaMigrated = await MigrateDatabaseSchemaAsync(eventData.TenantId);
await SeedDataAsync(
tenantId: eventData.TenantId,
adminEmail: IdentityServiceDbProperties.DefaultAdminEmailAddress,
adminPassword: IdentityServiceDbProperties.DefaultAdminPassword
);
Log.Information("IdentityService has acquired lock for db migration...");
await _localEventBus.PublishAsync(new ApplyDatabaseSeedsEto());
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
if (handle != null)
{
Log.Information("IdentityService is migrating database...");
await MigrateDatabaseSchemaAsync();
Log.Information("IdentityService is seeding data...");
await SeedDataAsync(
adminEmail: IdentityServiceDbProperties.DefaultAdminEmailAddress,
adminPassword: IdentityServiceDbProperties.DefaultAdminPassword
);
}
}
await _localEventBus.PublishAsync(new ApplyDatabaseSeedsEto());
}
public async Task HandleEventAsync(TenantCreatedEto eventData)
{
try
{
await MigrateDatabaseSchemaAsync(eventData.Id);
await SeedDataAsync(
tenantId: eventData.Id,
adminEmail: eventData.Properties.GetOrDefault(IdentityDataSeedContributor.AdminEmailPropertyName) ?? IdentityServiceDbProperties.DefaultAdminEmailAddress,
adminPassword: eventData.Properties.GetOrDefault(IdentityDataSeedContributor.AdminPasswordPropertyName) ?? IdentityServiceDbProperties.DefaultAdminPassword
);
}
catch (Exception ex)
{
await HandleErrorTenantCreatedAsync(eventData, ex);
}
}
private async Task SeedDataAsync(Guid? tenantId, string adminEmail, string adminPassword)
catch (Exception ex)
{
using (CurrentTenant.Change(tenantId))
{
if (tenantId == null)
{
Log.Information($"Seeding IdentityServer data...");
await _identityServerDataSeeder.SeedAsync();
}
Log.Information($"Seeding user data...");
await _identityDataSeeder.SeedAsync(
adminEmail,
adminPassword,
tenantId
);
}
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
}
}
}
private async Task SeedDataAsync(string adminEmail, string adminPassword)
{
Log.Information($"Seeding IdentityServer data...");
await _identityServerDataSeeder.SeedAsync();
Log.Information($"Seeding user data...");
await _identityDataSeeder.SeedAsync(
adminEmail,
adminPassword
);
}
}

9
services/identity/src/EShopOnAbp.IdentityService.HttpApi.Host/IdentityServiceHttpApiHostModule.cs

@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.IdentityService.DbMigrations;
using EShopOnAbp.IdentityService.EntityFrameworkCore;
using EShopOnAbp.Shared.Hosting.AspNetCore;
@ -12,9 +8,12 @@ using Microsoft.AspNetCore.Cors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace EShopOnAbp.IdentityService;

7
services/ordering/src/EShopOnAbp.OrderingService.Application.Contracts/Orders/OrderDto.cs

@ -6,10 +6,9 @@ namespace EShopOnAbp.OrderingService.Orders;
public class OrderDto : EntityDto<Guid>
{
public DateTime OrderDate { get; set; }
public int OrderNo {get;set;}
public int OrderStatusId { get; set; }
public string OrderStatus { get; set; }
public DateTime OrderDate { get; set; }
public int OrderNo { get; set; }
public OrderStatus OrderStatus { get; set; }
public string PaymentMethod { get; set; }
public BuyerDto Buyer { get; set; }
public OrderAddressDto Address { get; set; } = new();

15
services/ordering/src/EShopOnAbp.OrderingService.Application/Orders/OrderAppService.cs

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EShopOnAbp.OrderingService.Localization;
using EShopOnAbp.OrderingService.Localization;
using EShopOnAbp.OrderingService.Orders.Specifications;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Specifications;
using Volo.Abp.Users;
@ -34,9 +34,9 @@ public class OrderAppService : ApplicationService, IOrderAppService
public async Task<List<OrderDto>> GetMyOrdersAsync(GetMyOrdersInput input)
{
ISpecification<Order> specification = SpecificationFactory.Create(input.Filter);
var orders = await _orderRepository.GetOrdersByUserId(CurrentUser.GetId(), specification, true);
return CreateOrderDtoMapping(orders);
}
@ -106,8 +106,7 @@ public class OrderAppService : ApplicationService, IOrderAppService
Id = order.Id,
OrderNo = order.OrderNo,
OrderDate = order.OrderDate,
OrderStatus = order.OrderStatus.Name,
OrderStatusId = order.OrderStatus.Id,
OrderStatus = order.OrderStatus,
PaymentMethod = order.PaymentMethod
};
}

9
services/ordering/src/EShopOnAbp.OrderingService.Domain.Shared/Orders/OrderStatus.cs

@ -0,0 +1,9 @@
namespace EShopOnAbp.OrderingService.Orders;
public enum OrderStatus
{
Placed,
Paid,
Shipped,
Cancelled
}

12
services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/Order.cs

@ -1,8 +1,8 @@
using System;
using EShopOnAbp.PaymentService.PaymentRequests;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using EShopOnAbp.PaymentService.PaymentRequests;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
@ -10,7 +10,6 @@ namespace EShopOnAbp.OrderingService.Orders;
public class Order : AggregateRoot<Guid>
{
private int _orderStatusId;
public DateTime OrderDate { get; private set; }
public int OrderNo { get; private set; }
public string PaymentMethod { get; private set; }
@ -25,15 +24,14 @@ public class Order : AggregateRoot<Guid>
{
}
internal Order(Guid id, Buyer buyer, Address address, [NotNull]string paymentMethod, Guid? paymentRequestId = null) : base(id)
internal Order(Guid id, Buyer buyer, Address address, [NotNull] string paymentMethod, Guid? paymentRequestId = null) : base(id)
{
_orderStatusId = OrderStatus.Placed.Id;
OrderDate = DateTime.UtcNow;
OrderNo = GenerateOrderNo(id);
Buyer = buyer;
Address = address;
PaymentRequestId = paymentRequestId;
PaymentMethod = Check.NotNullOrEmpty(paymentMethod,nameof(paymentMethod),maxLength:OrderConstants.OrderPaymentMethodNameMaxLength);
PaymentMethod = Check.NotNullOrEmpty(paymentMethod, nameof(paymentMethod), maxLength: OrderConstants.OrderPaymentMethodNameMaxLength);
PaymentStatus = PaymentRequestState.Waiting.ToString(); // From PaymentService.Domain.Shared
OrderItems = new List<OrderItem>();
}

48
services/ordering/src/EShopOnAbp.OrderingService.Domain/Orders/OrderStatus.cs

@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp;
namespace EShopOnAbp.OrderingService.Orders;
public class OrderStatus : Enumeration
{
public static OrderStatus Placed = new OrderStatus(1, nameof(Placed).ToLowerInvariant());
public static OrderStatus Paid = new OrderStatus(2, nameof(Paid).ToLowerInvariant());
public static OrderStatus Shipped = new OrderStatus(3, nameof(Shipped).ToLowerInvariant());
public static OrderStatus Cancelled = new OrderStatus(4, nameof(Cancelled).ToLowerInvariant());
public OrderStatus(int id, string name) : base(id, name)
{
}
public static IEnumerable<OrderStatus> List() =>
new[] {Placed, Paid, Shipped, Cancelled};
public static OrderStatus FromName(string name)
{
var state = List()
.SingleOrDefault(s => String.Equals(s.Name, name, StringComparison.CurrentCultureIgnoreCase));
if (state == null)
{
throw new BusinessException(OrderingServiceErrorCodes.OrderingStatusNotFound)
.WithData("OrderStatus", String.Join(",", List().Select(s => s.Name)));
}
return state;
}
public static OrderStatus From(int id)
{
var state = List().SingleOrDefault(s => s.Id == id);
if (state == null)
{
throw new BusinessException(OrderingServiceErrorCodes.OrderingStatusNotFound)
.WithData("OrderStatus", String.Join(",", List().Select(s => s.Name)));
}
return state;
}
}

37
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/EntityFrameworkCore/OrderServiceDataSeedContributor.cs

@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.OrderingService.Orders;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.OrderingService.EntityFrameworkCore;
/// <summary>
/// DataSeedContributor for seeding pre-exist order status data.
/// This is a sample for seeding data without DbSet (non-aggregate data), without using repository.
/// </summary>
public class OrderServiceDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IOrderingServiceDbContext _dbContext;
public OrderServiceDataSeedContributor(IOrderingServiceDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task SeedAsync(DataSeedContext context)
{
await SeedOrderStatusAsync();
}
private async Task SeedOrderStatusAsync()
{
if (!await _dbContext.Set<OrderStatus>().AnyAsync())
{
await _dbContext.Set<OrderStatus>().AddRangeAsync(OrderStatus.List());
await _dbContext.SaveChangesAsync();
}
}
}

41
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/EntityFrameworkCore/OrderingServiceDbContext.cs

@ -1,6 +1,7 @@
using System;
using EShopOnAbp.OrderingService.Orders;
using EShopOnAbp.OrderingService.Orders;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using System;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;
@ -17,17 +18,17 @@ public class OrderingServiceDbContext : AbpDbContext<OrderingServiceDbContext>,
{
}
protected override void OnModelCreating(ModelBuilder builder)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(builder);
base.OnModelCreating(modelBuilder);
/* Include modules to your migration db context */
builder.ConfigureOrderingService();
modelBuilder.ConfigureOrderingService();
/* Configure your own tables/entities inside here */
builder.Entity<Order>(b =>
modelBuilder.Entity<Order>(b =>
{
b.ToTable(OrderingServiceDbProperties.DbTablePrefix + "Orders", OrderingServiceDbProperties.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
@ -38,16 +39,12 @@ public class OrderingServiceDbContext : AbpDbContext<OrderingServiceDbContext>,
b.OwnsOne(o => o.Address, a => { a.WithOwner(); });
b.OwnsOne(o => o.Buyer, a => { a.WithOwner(); });
b.Property<int>("_orderStatusId").UsePropertyAccessMode(PropertyAccessMode.Field)
.HasColumnName("OrderStatusId")
.IsRequired();
b.HasOne(q => q.OrderStatus).WithMany().HasForeignKey("_orderStatusId");
b.Navigation(q => q.OrderItems).UsePropertyAccessMode(PropertyAccessMode.Property);
b.Property(q => q.OrderStatus).HasConversion(new EnumToStringConverter<OrderStatus>());
});
builder.Entity<OrderItem>(b =>
modelBuilder.Entity<OrderItem>(b =>
{
b.ToTable(OrderingServiceDbProperties.DbTablePrefix + "OrderItems",
OrderingServiceDbProperties.DbSchema);
@ -62,23 +59,5 @@ public class OrderingServiceDbContext : AbpDbContext<OrderingServiceDbContext>,
b.Property(q => q.Units).IsRequired();
b.Property(q => q.PictureUrl).IsRequired(false);
});
builder.Entity<OrderStatus>(b =>
{
b.ToTable(OrderingServiceDbProperties.DbTablePrefix + "OrderStatus",
OrderingServiceDbProperties.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
b.HasKey(q => q.Id);
b.Property(q => q.Id)
.HasDefaultValue(1)
.ValueGeneratedNever()
.IsRequired();
b.Property(o => o.Name)
.HasMaxLength(OrderConstants.OrderStatusNameMaxLength)
.IsRequired();
});
}
}

182
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/20220317123922_RemovedOrderStatusTable.Designer.cs

@ -0,0 +1,182 @@
// <auto-generated />
using System;
using EShopOnAbp.OrderingService.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace EShopOnAbp.OrderingService.Migrations
{
[DbContext(typeof(OrderingServiceDbContext))]
[Migration("20220317123922_RemovedOrderStatusTable")]
partial class RemovedOrderStatusTable
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("ProductVersion", "6.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("ExtraProperties")
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<DateTime>("OrderDate")
.HasColumnType("timestamp without time zone");
b.Property<int>("OrderNo")
.HasColumnType("integer");
b.Property<string>("OrderStatus")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PaymentMethod")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<Guid?>("PaymentRequestId")
.HasColumnType("uuid");
b.Property<string>("PaymentStatus")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.ToTable("Orders", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderItem", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<decimal>("Discount")
.HasColumnType("numeric");
b.Property<Guid>("OrderId")
.HasColumnType("uuid");
b.Property<string>("PictureUrl")
.HasColumnType("text");
b.Property<string>("ProductCode")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("ProductId")
.HasColumnType("uuid");
b.Property<string>("ProductName")
.IsRequired()
.HasColumnType("text");
b.Property<decimal>("UnitPrice")
.HasColumnType("numeric");
b.Property<int>("Units")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("OrderId");
b.ToTable("OrderItems", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.OwnsOne("EShopOnAbp.OrderingService.Orders.Address", "Address", b1 =>
{
b1.Property<Guid>("OrderId")
.HasColumnType("uuid");
b1.Property<string>("City")
.HasColumnType("text");
b1.Property<string>("Country")
.HasColumnType("text");
b1.Property<string>("Description")
.HasColumnType("text");
b1.Property<string>("Street")
.HasColumnType("text");
b1.Property<string>("ZipCode")
.HasColumnType("text");
b1.HasKey("OrderId");
b1.ToTable("Orders");
b1.WithOwner()
.HasForeignKey("OrderId");
});
b.OwnsOne("EShopOnAbp.OrderingService.Orders.Buyer", "Buyer", b1 =>
{
b1.Property<Guid>("OrderId")
.HasColumnType("uuid");
b1.Property<string>("Email")
.HasColumnType("text");
b1.Property<Guid?>("Id")
.HasColumnType("uuid");
b1.Property<string>("Name")
.HasColumnType("text");
b1.HasKey("OrderId");
b1.ToTable("Orders");
b1.WithOwner()
.HasForeignKey("OrderId");
});
b.Navigation("Address");
b.Navigation("Buyer");
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderItem", b =>
{
b.HasOne("EShopOnAbp.OrderingService.Orders.Order", null)
.WithMany("OrderItems")
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.Navigation("OrderItems");
});
#pragma warning restore 612, 618
}
}
}

73
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/20220317123922_RemovedOrderStatusTable.cs

@ -0,0 +1,73 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EShopOnAbp.OrderingService.Migrations
{
public partial class RemovedOrderStatusTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Orders_OrderStatus_OrderStatusId",
table: "Orders");
migrationBuilder.DropTable(
name: "OrderStatus");
migrationBuilder.DropIndex(
name: "IX_Orders_OrderStatusId",
table: "Orders");
migrationBuilder.DropColumn(
name: "OrderStatusId",
table: "Orders");
migrationBuilder.AddColumn<string>(
name: "OrderStatus",
table: "Orders",
type: "text",
nullable: false,
defaultValue: "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "OrderStatus",
table: "Orders");
migrationBuilder.AddColumn<int>(
name: "OrderStatusId",
table: "Orders",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateTable(
name: "OrderStatus",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false, defaultValue: 1),
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OrderStatus", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_Orders_OrderStatusId",
table: "Orders",
column: "OrderStatusId");
migrationBuilder.AddForeignKey(
name: "FK_Orders_OrderStatus_OrderStatusId",
table: "Orders",
column: "OrderStatusId",
principalTable: "OrderStatus",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

36
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Migrations/OrderingServiceDbContextModelSnapshot.cs

@ -19,7 +19,7 @@ namespace EShopOnAbp.OrderingService.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "6.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -45,6 +45,10 @@ namespace EShopOnAbp.OrderingService.Migrations
b.Property<int>("OrderNo")
.HasColumnType("integer");
b.Property<string>("OrderStatus")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PaymentMethod")
.IsRequired()
.HasMaxLength(128)
@ -57,14 +61,8 @@ namespace EShopOnAbp.OrderingService.Migrations
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<int>("_orderStatusId")
.HasColumnType("integer")
.HasColumnName("OrderStatusId");
b.HasKey("Id");
b.HasIndex("_orderStatusId");
b.ToTable("Orders", (string)null);
});
@ -106,30 +104,8 @@ namespace EShopOnAbp.OrderingService.Migrations
b.ToTable("OrderItems", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderStatus", b =>
{
b.Property<int>("Id")
.HasColumnType("integer")
.HasDefaultValue(1);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.ToTable("OrderStatus", (string)null);
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.Order", b =>
{
b.HasOne("EShopOnAbp.OrderingService.Orders.OrderStatus", "OrderStatus")
.WithMany()
.HasForeignKey("_orderStatusId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.OwnsOne("EShopOnAbp.OrderingService.Orders.Address", "Address", b1 =>
{
b1.Property<Guid>("OrderId")
@ -183,8 +159,6 @@ namespace EShopOnAbp.OrderingService.Migrations
b.Navigation("Address");
b.Navigation("Buyer");
b.Navigation("OrderStatus");
});
modelBuilder.Entity("EShopOnAbp.OrderingService.Orders.OrderItem", b =>

14
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/EfCoreOrderRepository.cs

@ -1,11 +1,11 @@
using System;
using EShopOnAbp.OrderingService.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using EShopOnAbp.OrderingService.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
@ -20,14 +20,6 @@ public class EfCoreOrderRepository : EfCoreRepository<OrderingServiceDbContext,
{
}
public override async Task<Order> InsertAsync(Order entity, bool autoSave = false,
CancellationToken cancellationToken = default)
{
var newEntity = await base.InsertAsync(entity, autoSave, GetCancellationToken(cancellationToken));
await EnsurePropertyLoadedAsync(newEntity, o => o.OrderStatus, GetCancellationToken(cancellationToken));
return newEntity;
}
public async Task<List<Order>> GetOrdersByUserId(
Guid userId,
ISpecification<Order> spec,

3
services/ordering/src/EShopOnAbp.OrderingService.EntityFrameworkCore/Orders/OrderEfCoreQueryableExtensions.cs

@ -15,7 +15,6 @@ public static class OrderEfCoreQueryableExtensions
return queryable
.Include(x => x.Address)
.Include(x => x.Buyer)
.Include(x => x.OrderItems)
.Include(x => x.OrderStatus);
.Include(x => x.OrderItems);
}
}

13
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/Controllers/HomeController.cs

@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace EShopOnAbp.OrderingService.Controllers
namespace EShopOnAbp.OrderingService.Controllers;
public class HomeController : AbpController
{
public class HomeController : AbpController
public ActionResult Index()
{
public ActionResult Index()
{
return Redirect("~/swagger");
}
return Redirect("~/swagger");
}
}
}

33
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/DbMigrations/OrderingServiceDatabaseMigrationChecker.cs

@ -5,23 +5,22 @@ using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.OrderingService.DbMigrations
namespace EShopOnAbp.OrderingService.DbMigrations;
public class OrderingServiceDatabaseMigrationChecker
: PendingEfCoreMigrationsChecker<OrderingServiceDbContext>
{
public class OrderingServiceDatabaseMigrationChecker : PendingEfCoreMigrationsChecker<OrderingServiceDbContext>
public OrderingServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
OrderingServiceDbProperties.ConnectionStringName)
{
public OrderingServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
OrderingServiceDbProperties.ConnectionStringName)
{
}
}
}
}

88
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/DbMigrations/OrderingServiceDatabaseMigrationEventHandler.cs

@ -1,58 +1,70 @@
using EShopOnAbp.OrderingService.EntityFrameworkCore;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using System;
using System.Threading.Tasks;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using Microsoft.Extensions.Logging;
using Serilog;
using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.OrderingService.DbMigrations
{
public class OrderingServiceDatabaseMigrationEventHandler
: DatabaseEfCoreMigrationEventHandler<OrderingServiceDbContext>,
namespace EShopOnAbp.OrderingService.DbMigrations;
public class OrderingServiceDatabaseMigrationEventHandler
: DatabaseEfCoreMigrationEventHandler<OrderingServiceDbContext>,
IDistributedEventHandler<ApplyDatabaseMigrationsEto>
{
private readonly IDataSeeder _dataSeeder;
public OrderingServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus,
IDataSeeder dataSeeder,
IAbpDistributedLock distributedLockProvider)
: base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
OrderingServiceDbProperties.ConnectionStringName,
distributedLockProvider)
{
private readonly IDataSeeder _dataSeeder;
public OrderingServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus,
IDataSeeder dataSeeder)
: base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
OrderingServiceDbProperties.ConnectionStringName)
_dataSeeder = dataSeeder;
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
{
if (eventData.DatabaseName != DatabaseName)
{
_dataSeeder = dataSeeder;
return;
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
if (eventData.TenantId != null)
{
if (eventData.DatabaseName != DatabaseName)
{
return;
}
return;
}
if (eventData.TenantId != null)
try
{
await using (var handle = await DistributedLockProvider.TryAcquireAsync(DatabaseName))
{
return;
}
Log.Information("OrderingService has acquired lock for db migration...");
try
{
await MigrateDatabaseSchemaAsync(null);
Logger.LogInformation("Starting OrderingService DataSeeder...");
await _dataSeeder.SeedAsync();
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
if (handle != null)
{
Log.Information("OrderingService is migrating database...");
await MigrateDatabaseSchemaAsync();
Log.Information("OrderingService is seeding data...");
await _dataSeeder.SeedAsync();
}
}
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
}
}
}
}

3
services/ordering/src/EShopOnAbp.OrderingService.HttpApi.Host/OrderingServiceHttpApiHostModule.cs

@ -4,17 +4,16 @@ using EShopOnAbp.Shared.Hosting.AspNetCore;
using EShopOnAbp.Shared.Hosting.Microservices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace EShopOnAbp.OrderingService;

19
services/ordering/test/EShopOnAbp.OrderingService.EntityFrameworkCore.Tests/EntityFrameworkCore/Orders/OrderRepository_Tests.cs

@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EShopOnAbp.OrderingService.Orders;
using EShopOnAbp.OrderingService.Orders;
using EShopOnAbp.OrderingService.Orders.Specifications;
using EShopOnAbp.OrderingService.Samples;
using Microsoft.EntityFrameworkCore;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace EShopOnAbp.OrderingService.EntityFrameworkCore.Orders;
@ -26,14 +25,6 @@ public class OrderRepository_Tests : SampleRepository_Tests<OrderingServiceEntit
_testData = GetRequiredService<TestData>();
}
[Fact]
public async Task Should_Get_OrderStatus()
{
var dbSet = _dbContext.Set<OrderStatus>();
var statusList = await dbSet.ToListAsync();
statusList.Count.ShouldNotBe(0);
}
[Fact]
public async Task Should_Get_User_Orders()
{

13
services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/Controllers/HomeController.cs

@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace EShopOnAbp.PaymentService.Controllers
namespace EShopOnAbp.PaymentService.Controllers;
public class HomeController : AbpController
{
public class HomeController : AbpController
public ActionResult Index()
{
public ActionResult Index()
{
return Redirect("~/swagger");
}
return Redirect("~/swagger");
}
}
}

33
services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/DbMigrations/PaymentServiceDatabaseMigrationChecker.cs

@ -5,23 +5,22 @@ using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.PaymentService.DbMigrations
namespace EShopOnAbp.PaymentService.DbMigrations;
public class PaymentServiceDatabaseMigrationChecker
: PendingEfCoreMigrationsChecker<PaymentServiceDbContext>
{
public class PaymentServiceDatabaseMigrationChecker : PendingEfCoreMigrationsChecker<PaymentServiceDbContext>
public PaymentServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
PaymentServiceDbProperties.ConnectionStringName)
{
public PaymentServiceDatabaseMigrationChecker(
IUnitOfWorkManager unitOfWorkManager,
IServiceProvider serviceProvider,
ICurrentTenant currentTenant,
IDistributedEventBus distributedEventBus)
: base(
unitOfWorkManager,
serviceProvider,
currentTenant,
distributedEventBus,
PaymentServiceDbProperties.ConnectionStringName)
{
}
}
}
}

76
services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/DbMigrations/PaymentServiceDatabaseMigrationEventHandler.cs

@ -1,52 +1,64 @@
using EShopOnAbp.PaymentService.EntityFrameworkCore;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using System;
using System.Threading.Tasks;
using EShopOnAbp.Shared.Hosting.Microservices.DbMigrations.EfCore;
using Serilog;
using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace EShopOnAbp.PaymentService.DbMigrations
{
public class PaymentServiceDatabaseMigrationEventHandler
: DatabaseEfCoreMigrationEventHandler<PaymentServiceDbContext>,
namespace EShopOnAbp.PaymentService.DbMigrations;
public class PaymentServiceDatabaseMigrationEventHandler
: DatabaseEfCoreMigrationEventHandler<PaymentServiceDbContext>,
IDistributedEventHandler<ApplyDatabaseMigrationsEto>
{
public PaymentServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus,
IAbpDistributedLock distributedLockProvider)
: base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
PaymentServiceDbProperties.ConnectionStringName,
distributedLockProvider)
{
public PaymentServiceDatabaseMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus)
: base(
currentTenant,
unitOfWorkManager,
tenantStore,
distributedEventBus,
PaymentServiceDbProperties.ConnectionStringName)
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
{
if (eventData.DatabaseName != DatabaseName)
{
return;
}
public async Task HandleEventAsync(ApplyDatabaseMigrationsEto eventData)
if (eventData.TenantId != null)
{
if (eventData.DatabaseName != DatabaseName)
{
return;
}
return;
}
if (eventData.TenantId != null)
{
return;
}
try
{
Log.Information("PaymentService has acquired lock for db migration...");
try
await using (var handle = await DistributedLockProvider.TryAcquireAsync(DatabaseName))
{
await MigrateDatabaseSchemaAsync(null);
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
if (handle != null)
{
Log.Information("PaymentService is migrating database...");
await MigrateDatabaseSchemaAsync();
}
}
}
catch (Exception ex)
{
await HandleErrorOnApplyDatabaseMigrationAsync(eventData, ex);
}
}
}
}

3
services/payment/src/EShopOnAbp.PaymentService.HttpApi.Host/PaymentServiceHttpApiHostModule.cs

@ -4,16 +4,15 @@ using EShopOnAbp.Shared.Hosting.AspNetCore;
using EShopOnAbp.Shared.Hosting.Microservices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace EShopOnAbp.PaymentService;

19
shared/EShopOnAbp.Shared.Hosting.AspNetCore/EShopOnAbpSharedHostingAspNetCoreModule.cs

@ -2,17 +2,16 @@
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
namespace EShopOnAbp.Shared.Hosting.AspNetCore
namespace EShopOnAbp.Shared.Hosting.AspNetCore;
[DependsOn(
typeof(EShopOnAbpSharedHostingModule),
typeof(AbpSwashbuckleModule),
typeof(AbpAspNetCoreSerilogModule)
)]
public class EShopOnAbpSharedHostingAspNetCoreModule : AbpModule
{
[DependsOn(
typeof(EShopOnAbpSharedHostingModule),
typeof(AbpSwashbuckleModule),
typeof(AbpAspNetCoreSerilogModule)
)]
public class EShopOnAbpSharedHostingAspNetCoreModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
}
}

55
shared/EShopOnAbp.Shared.Hosting.AspNetCore/SerilogConfigurationHelper.cs

@ -1,40 +1,39 @@
using Serilog;
using Serilog.Events;
namespace EShopOnAbp.Shared.Hosting.AspNetCore
namespace EShopOnAbp.Shared.Hosting.AspNetCore;
public static class SerilogConfigurationHelper
{
public static class SerilogConfigurationHelper
public static void Configure(string applicationName)
{
public static void Configure(string applicationName)
{
// TODO: Uncomment following lines for ElasticSearch configuration
// var configuration = new ConfigurationBuilder()
// .SetBasePath(Directory.GetCurrentDirectory())
// .AddJsonFile("appsettings.json")
// .AddEnvironmentVariables()
// .Build();
// TODO: Uncomment following lines for ElasticSearch configuration
// var configuration = new ConfigurationBuilder()
// .SetBasePath(Directory.GetCurrentDirectory())
// .AddJsonFile("appsettings.json")
// .AddEnvironmentVariables()
// .Build();
Log.Logger = new LoggerConfiguration()
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", $"{applicationName}")
.WriteTo.Async(c => c.File("Logs/logs.txt"))
// TODO: Uncomment following lines for ElasticSearch configuration
// .WriteTo.Elasticsearch(
// new ElasticsearchSinkOptions(new Uri(configuration["ElasticSearch:Url"]))
// {
// AutoRegisterTemplate = true,
// AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6,
// IndexFormat = "MyProjectName-log-{0:yyyy.MM}"
// })
.WriteTo.Async(c => c.Console())
.CreateLogger();
}
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", $"{applicationName}")
.WriteTo.Async(c => c.File("Logs/logs.txt"))
// TODO: Uncomment following lines for ElasticSearch configuration
// .WriteTo.Elasticsearch(
// new ElasticsearchSinkOptions(new Uri(configuration["ElasticSearch:Url"]))
// {
// AutoRegisterTemplate = true,
// AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6,
// IndexFormat = "MyProjectName-log-{0:yyyy.MM}"
// })
.WriteTo.Async(c => c.Console())
.CreateLogger();
}
}

55
shared/EShopOnAbp.Shared.Hosting.AspNetCore/SwaggerConfigurationHelper.cs

@ -3,40 +3,39 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Volo.Abp.Modularity;
namespace EShopOnAbp.Shared.Hosting.AspNetCore
namespace EShopOnAbp.Shared.Hosting.AspNetCore;
public static class SwaggerConfigurationHelper
{
public static class SwaggerConfigurationHelper
public static void Configure(
ServiceConfigurationContext context,
string apiTitle
)
{
public static void Configure(
ServiceConfigurationContext context,
string apiTitle
)
context.Services.AddSwaggerGen(options =>
{
context.Services.AddSwaggerGen(options =>
options.SwaggerDoc("v1", new OpenApiInfo {Title = apiTitle, Version = "v1"});
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
public static void ConfigureWithAuth(
ServiceConfigurationContext context,
string authority,
Dictionary<string, string> scopes,
string apiTitle,
string apiVersion = "v1",
string apiName = "v1"
)
{
context.Services.AddAbpSwaggerGenWithOAuth(
authority: authority,
scopes: scopes,
options =>
{
options.SwaggerDoc("v1", new OpenApiInfo {Title = apiTitle, Version = "v1"});
options.SwaggerDoc(apiName, new OpenApiInfo { Title = apiTitle, Version = apiVersion });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
public static void ConfigureWithAuth(
ServiceConfigurationContext context,
string authority,
Dictionary<string, string> scopes,
string apiTitle,
string apiVersion = "v1",
string apiName = "v1"
)
{
context.Services.AddAbpSwaggerGenWithOAuth(
authority: authority,
scopes: scopes,
options =>
{
options.SwaggerDoc(apiName, new OpenApiInfo { Title = apiTitle, Version = apiVersion });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
});
}
}
}

111
shared/EShopOnAbp.Shared.Hosting.Microservices/DbMigrations/EfCore/DatabaseEfCoreMigrationEventHandler.cs

@ -1,14 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EventBus.Distributed;
@ -22,26 +23,29 @@ public abstract class DatabaseEfCoreMigrationEventHandler<TDbContext> : Database
{
protected const string TryCountPropertyName = "TryCount";
protected const int MaxEventTryCount = 3;
protected ICurrentTenant CurrentTenant { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected ITenantStore TenantStore { get; }
protected IDistributedEventBus DistributedEventBus { get; }
protected ILogger<DatabaseEfCoreMigrationEventHandler<TDbContext>> Logger { get; set; }
protected string DatabaseName { get; }
protected IAbpDistributedLock DistributedLockProvider { get; }
protected DatabaseEfCoreMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus,
string databaseName)
string databaseName,
IAbpDistributedLock distributedLockProvider
)
{
CurrentTenant = currentTenant;
UnitOfWorkManager = unitOfWorkManager;
TenantStore = tenantStore;
DatabaseName = databaseName;
DistributedEventBus = distributedEventBus;
DistributedLockProvider = distributedLockProvider;
Logger = NullLogger<DatabaseEfCoreMigrationEventHandler<TDbContext>>.Instance;
}
@ -50,47 +54,32 @@ public abstract class DatabaseEfCoreMigrationEventHandler<TDbContext> : Database
/// Apply pending EF Core schema migrations to the database.
/// Returns true if any migration has applied.
/// </summary>
protected virtual async Task<bool> MigrateDatabaseSchemaAsync(Guid? tenantId)
protected virtual async Task<bool> MigrateDatabaseSchemaAsync()
{
var result = false;
using (CurrentTenant.Change(tenantId))
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false))
{
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false))
async Task<bool> MigrateDatabaseSchemaWithDbContextAsync()
{
async Task<bool> MigrateDatabaseSchemaWithDbContextAsync()
{
var dbContext = await uow.ServiceProvider
.GetRequiredService<IDbContextProvider<TDbContext>>()
.GetDbContextAsync();
if ((await dbContext.Database.GetPendingMigrationsAsync()).Any())
{
await dbContext.Database.MigrateAsync();
return true;
}
return false;
}
var dbContext = await uow.ServiceProvider
.GetRequiredService<IDbContextProvider<TDbContext>>()
.GetDbContextAsync();
if (tenantId == null)
if ((await dbContext.Database.GetPendingMigrationsAsync()).Any())
{
//Migrating the host database
result = await MigrateDatabaseSchemaWithDbContextAsync();
}
else
{
var tenantConfiguration = await TenantStore.FindAsync(tenantId.Value);
if (!tenantConfiguration.ConnectionStrings.Default.IsNullOrWhiteSpace() ||
!tenantConfiguration.ConnectionStrings.GetOrDefault(DatabaseName).IsNullOrWhiteSpace())
{
//Migrating the tenant database (only if tenant has a separate database)
result = await MigrateDatabaseSchemaWithDbContextAsync();
}
await dbContext.Database.MigrateAsync();
return true;
}
await uow.CompleteAsync();
return false;
}
//Migrating the host database
Log.Information($"There is no tenant. Migrating {DatabaseName}...");
result = await MigrateDatabaseSchemaWithDbContextAsync();
await uow.CompleteAsync();
}
return result;
@ -113,56 +102,12 @@ public abstract class DatabaseEfCoreMigrationEventHandler<TDbContext> : Database
}
else
{
Log.Error(
Log.Warning(
$"Could not apply database migrations. Canceling the operation. TenantId = {eventData.TenantId}, DatabaseName = {eventData.DatabaseName}.");
Log.Error(exception.ToString());
}
}
protected virtual async Task HandleErrorTenantCreatedAsync(
TenantCreatedEto eventData,
Exception exception)
{
var tryCount = IncrementEventTryCount(eventData);
if (tryCount <= MaxEventTryCount)
{
Logger.LogWarning(
$"Could not perform tenant created event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception, LogLevel.Warning);
await Task.Delay(RandomHelper.GetRandom(5000, 15000));
await DistributedEventBus.PublishAsync(eventData);
}
else
{
Logger.LogError(
$"Could not perform tenant created event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception);
}
}
protected virtual async Task HandleErrorTenantConnectionStringUpdatedAsync(
TenantConnectionStringUpdatedEto eventData,
Exception exception)
{
var tryCount = IncrementEventTryCount(eventData);
if (tryCount <= MaxEventTryCount)
{
Logger.LogWarning(
$"Could not perform tenant connection string updated event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception, LogLevel.Warning);
await Task.Delay(RandomHelper.GetRandom(5000, 15000));
await DistributedEventBus.PublishAsync(eventData);
}
else
{
Logger.LogError(
$"Could not perform tenant connection string updated event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception);
}
}
private static int GetEventTryCount(EtoBase eventData)
{
var tryCountAsString = eventData.Properties.GetOrDefault(TryCountPropertyName);

129
shared/EShopOnAbp.Shared.Hosting.Microservices/DbMigrations/MongoDb/DatabaseMongoDbMigrationEventHandler.cs

@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.MongoDB;
@ -27,16 +27,20 @@ public abstract class DatabaseMongoDbMigrationEventHandler<TDbContext> : Databas
protected ITenantStore TenantStore { get; }
protected IDistributedEventBus DistributedEventBus { get; }
protected ILogger<DatabaseMongoDbMigrationEventHandler<TDbContext>> Logger { get; set; }
protected IServiceProvider ServiceProvider { get; }
protected string DatabaseName { get; }
protected IAbpDistributedLock DistributedLockProvider { get; }
protected DatabaseMongoDbMigrationEventHandler(
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager,
ITenantStore tenantStore,
IDistributedEventBus distributedEventBus,
string databaseName, IServiceProvider serviceProvider)
string databaseName,
IServiceProvider serviceProvider,
IAbpDistributedLock distributedLockProvider
)
{
CurrentTenant = currentTenant;
UnitOfWorkManager = unitOfWorkManager;
@ -44,6 +48,7 @@ public abstract class DatabaseMongoDbMigrationEventHandler<TDbContext> : Databas
DatabaseName = databaseName;
ServiceProvider = serviceProvider;
DistributedEventBus = distributedEventBus;
DistributedLockProvider = distributedLockProvider;
Logger = NullLogger<DatabaseMongoDbMigrationEventHandler<TDbContext>>.Instance;
}
@ -52,62 +57,46 @@ public abstract class DatabaseMongoDbMigrationEventHandler<TDbContext> : Databas
/// Apply pending EF Core schema migrations to the database.
/// Returns true if any migration has applied.
/// </summary>
protected virtual async Task<bool> MigrateDatabaseSchemaAsync(Guid? tenantId)
protected virtual async Task<bool> MigrateDatabaseSchemaAsync()
{
var result = false;
using (CurrentTenant.Change(tenantId))
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false))
{
using (var uow = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: false))
async Task<bool> MigrateDatabaseSchemaWithDbContextAsync()
{
async Task<bool> MigrateDatabaseSchemaWithDbContextAsync()
{
var dbContexts = ServiceProvider.GetServices<IAbpMongoDbContext>();
var connectionStringResolver = ServiceProvider.GetRequiredService<IConnectionStringResolver>();
var dbContexts = ServiceProvider.GetServices<IAbpMongoDbContext>();
var connectionStringResolver = ServiceProvider.GetRequiredService<IConnectionStringResolver>();
foreach (var dbContext in dbContexts)
foreach (var dbContext in dbContexts)
{
var connectionString =
await connectionStringResolver.ResolveAsync(
ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType()));
if (connectionString.IsNullOrWhiteSpace())
{
var connectionString =
await connectionStringResolver.ResolveAsync(
ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType()));
if (connectionString.IsNullOrWhiteSpace())
{
continue;
}
var mongoUrl = new MongoUrl(connectionString);
var databaseName = mongoUrl.DatabaseName;
var client = new MongoClient(mongoUrl);
if (databaseName.IsNullOrWhiteSpace())
{
databaseName = ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType());
}
(dbContext as AbpMongoDbContext)?.InitializeCollections(client.GetDatabase(databaseName));
continue;
}
return true;
}
var mongoUrl = new MongoUrl(connectionString);
var databaseName = mongoUrl.DatabaseName;
var client = new MongoClient(mongoUrl);
if (tenantId == null)
{
//Migrating the host database
result = await MigrateDatabaseSchemaWithDbContextAsync();
}
else
{
var tenantConfiguration = await TenantStore.FindAsync(tenantId.Value);
if (!tenantConfiguration.ConnectionStrings.Default.IsNullOrWhiteSpace() ||
!tenantConfiguration.ConnectionStrings.GetOrDefault(DatabaseName).IsNullOrWhiteSpace())
if (databaseName.IsNullOrWhiteSpace())
{
//Migrating the tenant database (only if tenant has a separate database)
result = await MigrateDatabaseSchemaWithDbContextAsync();
databaseName = ConnectionStringNameAttribute.GetConnStringName(dbContext.GetType());
}
(dbContext as AbpMongoDbContext)?.InitializeCollections(client.GetDatabase(databaseName));
}
await uow.CompleteAsync();
return true;
}
//Migrating the host database
result = await MigrateDatabaseSchemaWithDbContextAsync();
await uow.CompleteAsync();
}
return result;
@ -135,50 +124,6 @@ public abstract class DatabaseMongoDbMigrationEventHandler<TDbContext> : Databas
}
}
protected virtual async Task HandleErrorTenantCreatedAsync(
TenantCreatedEto eventData,
Exception exception)
{
var tryCount = IncrementEventTryCount(eventData);
if (tryCount <= MaxEventTryCount)
{
Logger.LogWarning(
$"Could not perform tenant created event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception, LogLevel.Warning);
await Task.Delay(RandomHelper.GetRandom(5000, 15000));
await DistributedEventBus.PublishAsync(eventData);
}
else
{
Logger.LogError(
$"Could not perform tenant created event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception);
}
}
protected virtual async Task HandleErrorTenantConnectionStringUpdatedAsync(
TenantConnectionStringUpdatedEto eventData,
Exception exception)
{
var tryCount = IncrementEventTryCount(eventData);
if (tryCount <= MaxEventTryCount)
{
Logger.LogWarning(
$"Could not perform tenant connection string updated event. Re-queueing the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception, LogLevel.Warning);
await Task.Delay(RandomHelper.GetRandom(5000, 15000));
await DistributedEventBus.PublishAsync(eventData);
}
else
{
Logger.LogError(
$"Could not perform tenant connection string updated event. Canceling the operation. TenantId = {eventData.Id}, TenantName = {eventData.Name}.");
Logger.LogException(exception);
}
}
private static int GetEventTryCount(EtoBase eventData)
{
var tryCountAsString = eventData.Properties.GetOrDefault(TryCountPropertyName);

7
shared/EShopOnAbp.Shared.Hosting.Microservices/DbMigrations/PendingMigrationsCheckerBase.cs

@ -1,8 +1,7 @@
using Volo.Abp.DependencyInjection;
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations
namespace EShopOnAbp.Shared.Hosting.Microservices.DbMigrations;
public abstract class PendingMigrationsCheckerBase : ITransientDependency
{
public abstract class PendingMigrationsCheckerBase : ITransientDependency
{
}
}

1
shared/EShopOnAbp.Shared.Hosting.Microservices/EShopOnAbp.Shared.Hosting.Microservices.csproj

@ -9,6 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DistributedLock.Redis" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />

59
shared/EShopOnAbp.Shared.Hosting.Microservices/EShopOnAbpSharedHostingMicroservicesModule.cs

@ -1,5 +1,7 @@
using EShopOnAbp.AdministrationService.EntityFrameworkCore;
using EShopOnAbp.Shared.Hosting.AspNetCore;
using Medallion.Threading;
using Medallion.Threading.Redis;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;
@ -11,37 +13,42 @@ using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
namespace EShopOnAbp.Shared.Hosting.Microservices
namespace EShopOnAbp.Shared.Hosting.Microservices;
[DependsOn(
typeof(EShopOnAbpSharedHostingAspNetCoreModule),
typeof(AbpBackgroundJobsRabbitMqModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpEventBusRabbitMqModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(AdministrationServiceEntityFrameworkCoreModule)
)]
public class EShopOnAbpSharedHostingMicroservicesModule : AbpModule
{
[DependsOn(
typeof(EShopOnAbpSharedHostingAspNetCoreModule),
typeof(AbpBackgroundJobsRabbitMqModule),
typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpEventBusRabbitMqModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(AdministrationServiceEntityFrameworkCoreModule)
)]
public class EShopOnAbpSharedHostingMicroservicesModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
public override void ConfigureServices(ServiceConfigurationContext context)
var configuration = context.Services.GetConfiguration();
Configure<AbpMultiTenancyOptions>(options =>
{
var configuration = context.Services.GetConfiguration();
var hostingEnvironment = context.Services.GetHostingEnvironment();
options.IsEnabled = true;
});
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = true;
});
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "EShopOnAbp:";
});
Configure<AbpDistributedCacheOptions>(options =>
{
options.KeyPrefix = "EShopOnAbp:";
});
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "EShopOnAbp-Protection-Keys");
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "EShopOnAbp-Protection-Keys");
}
context.Services.AddSingleton<IDistributedLockProvider>(sp =>
{
var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
return new RedisDistributedSynchronizationProvider(connection.GetDatabase());
});
}
}

29
shared/EShopOnAbp.Shared.Hosting.Microservices/JwtBearerConfigurationHelper.cs

@ -3,23 +3,22 @@ using Microsoft.Extensions.DependencyInjection;
using System;
using Volo.Abp.Modularity;
namespace EShopOnAbp.Shared.Hosting.Microservices
namespace EShopOnAbp.Shared.Hosting.Microservices;
public static class JwtBearerConfigurationHelper
{
public static class JwtBearerConfigurationHelper
public static void Configure(
ServiceConfigurationContext context,
string audience)
{
public static void Configure(
ServiceConfigurationContext context,
string audience)
{
var configuration = context.Services.GetConfiguration();
var configuration = context.Services.GetConfiguration();
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.Audience = audience;
});
}
context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
options.Audience = audience;
});
}
}

39
shared/EShopOnAbp.Shared.Localization/EShopOnAbpSharedLocalizationModule.cs

@ -5,30 +5,29 @@ using Volo.Abp.Validation;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
namespace EShopOnAbp
namespace EShopOnAbp;
[DependsOn(
typeof(AbpValidationModule)
)]
public class EShopOnAbpSharedLocalizationModule : AbpModule
{
[DependsOn(
typeof(AbpValidationModule)
)]
public class EShopOnAbpSharedLocalizationModule : AbpModule
public override void ConfigureServices(ServiceConfigurationContext context)
{
public override void ConfigureServices(ServiceConfigurationContext context)
Configure<AbpVirtualFileSystemOptions>(options =>
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EShopOnAbpSharedLocalizationModule>();
});
options.FileSets.AddEmbedded<EShopOnAbpSharedLocalizationModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<EShopOnAbpResource>("en")
.AddBaseTypes(
typeof(AbpValidationResource)
).AddVirtualJson("/Localization/EShopOnAbp");
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<EShopOnAbpResource>("en")
.AddBaseTypes(
typeof(AbpValidationResource)
).AddVirtualJson("/Localization/EShopOnAbp");
options.DefaultResourceType = typeof(EShopOnAbpResource);
});
}
options.DefaultResourceType = typeof(EShopOnAbpResource);
});
}
}
Loading…
Cancel
Save