first commit

This commit is contained in:
2025-09-08 14:15:45 +08:00
commit 0ecba9619f
622 changed files with 37941 additions and 0 deletions

View File

@ -0,0 +1,28 @@
using Volo.Abp.Account;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(DispatchDomainSharedModule),
typeof(AbpAccountApplicationContractsModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpIdentityApplicationContractsModule),
typeof(AbpPermissionManagementApplicationContractsModule),
typeof(AbpSettingManagementApplicationContractsModule),
typeof(AbpTenantManagementApplicationContractsModule),
typeof(AbpObjectExtendingModule)
)]
public class DispatchApplicationContractsModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
DispatchDtoExtensions.Configure();
}
}

View File

@ -0,0 +1,28 @@
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace KonSoft.Dispatch;
public static class DispatchDtoExtensions
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
/* You can add extension properties to DTOs
* defined in the depended modules.
*
* Example:
*
* ObjectExtensionManager.Instance
* .AddOrUpdateProperty<IdentityRoleDto, string>("Title");
*
* See the documentation for more:
* https://docs.abp.io/en/abp/latest/Object-Extensions
*/
});
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\KonSoft.Dispatch.Domain.Shared\KonSoft.Dispatch.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.ObjectExtending" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Account.Application.Contracts" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Identity.Application.Contracts" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application.Contracts" Version="8.3.4" />
<PackageReference Include="Volo.Abp.TenantManagement.Application.Contracts" Version="8.3.4" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application.Contracts" Version="8.3.4" />
<PackageReference Include="Volo.Abp.SettingManagement.Application.Contracts" Version="8.3.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,20 @@
using KonSoft.Dispatch.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace KonSoft.Dispatch.Permissions;
public class DispatchPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var myGroup = context.AddGroup(DispatchPermissions.GroupName);
//Define your own permissions here. Example:
//myGroup.AddPermission(DispatchPermissions.MyPermission1, L("Permission:MyPermission1"));
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<DispatchResource>(name);
}
}

View File

@ -0,0 +1,9 @@
namespace KonSoft.Dispatch.Permissions;
public static class DispatchPermissions
{
public const string GroupName = "Dispatch";
//Add your own permission names. Example:
//public const string MyPermission1 = GroupName + ".MyPermission1";
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using KonSoft.Dispatch.Localization;
using Volo.Abp.Application.Services;
namespace KonSoft.Dispatch;
/* Inherit your application services from this class.
*/
public abstract class DispatchAppService : ApplicationService
{
protected DispatchAppService()
{
LocalizationResource = typeof(DispatchResource);
}
}

View File

@ -0,0 +1,13 @@
using AutoMapper;
namespace KonSoft.Dispatch;
public class DispatchApplicationAutoMapperProfile : Profile
{
public DispatchApplicationAutoMapperProfile()
{
/* You can configure your AutoMapper mapping configuration here.
* Alternatively, you can split your mapping configurations
* into multiple profile classes for a better organization. */
}
}

View File

@ -0,0 +1,31 @@
using Volo.Abp.Account;
using Volo.Abp.AutoMapper;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(DispatchDomainModule),
typeof(AbpAccountApplicationModule),
typeof(DispatchApplicationContractsModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpTenantManagementApplicationModule),
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpSettingManagementApplicationModule)
)]
public class DispatchApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>
{
options.AddMaps<DispatchApplicationModule>();
});
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\KonSoft.Dispatch.Domain\KonSoft.Dispatch.Domain.csproj" />
<ProjectReference Include="..\KonSoft.Dispatch.Application.Contracts\KonSoft.Dispatch.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Account.Application" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Identity.Application" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="8.3.4" />
<PackageReference Include="Volo.Abp.TenantManagement.Application" Version="8.3.4" />
<PackageReference Include="Volo.Abp.FeatureManagement.Application" Version="8.3.4" />
<PackageReference Include="Volo.Abp.SettingManagement.Application" Version="8.3.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleToAttribute("KonSoft.Dispatch.Application.Tests")]

View File

@ -0,0 +1,51 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using KonSoft.Dispatch.Data;
using Serilog;
using Volo.Abp;
using Volo.Abp.Data;
namespace KonSoft.Dispatch.DbMigrator;
public class DbMigratorHostedService : IHostedService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly IConfiguration _configuration;
public DbMigratorHostedService(IHostApplicationLifetime hostApplicationLifetime, IConfiguration configuration)
{
_hostApplicationLifetime = hostApplicationLifetime;
_configuration = configuration;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var application = await AbpApplicationFactory.CreateAsync<DispatchDbMigratorModule>(options =>
{
options.Services.ReplaceConfiguration(_configuration);
options.UseAutofac();
options.Services.AddLogging(c => c.AddSerilog());
options.AddDataMigrationEnvironment();
}))
{
await application.InitializeAsync();
await application
.ServiceProvider
.GetRequiredService<DispatchDbMigrationService>()
.MigrateAsync();
await application.ShutdownAsync();
_hostApplicationLifetime.StopApplication();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,21 @@
using KonSoft.Dispatch.EntityFrameworkCore;
using Volo.Abp.Autofac;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.Modularity;
namespace KonSoft.Dispatch.DbMigrator;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpCachingStackExchangeRedisModule),
typeof(DispatchEntityFrameworkCoreModule),
typeof(DispatchApplicationContractsModule)
)]
public class DispatchDbMigratorModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDistributedCacheOptions>(options => { options.KeyPrefix = "Dispatch:"; });
}
}

View File

@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Remove="appsettings.secrets.json" />
<Content Include="appsettings.secrets.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="8.3.4" />
<ProjectReference Include="..\KonSoft.Dispatch.Application.Contracts\KonSoft.Dispatch.Application.Contracts.csproj" />
<ProjectReference Include="..\KonSoft.Dispatch.EntityFrameworkCore\KonSoft.Dispatch.EntityFrameworkCore.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Logs\**" />
<Content Remove="Logs\**" />
<EmbeddedResource Remove="Logs\**" />
<None Remove="Logs\**" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,41 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
namespace KonSoft.Dispatch.DbMigrator;
class Program
{
static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning)
#if DEBUG
.MinimumLevel.Override("KonSoft.Dispatch", LogEventLevel.Debug)
#else
.MinimumLevel.Override("KonSoft.Dispatch", LogEventLevel.Information)
#endif
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
await CreateHostBuilder(args).RunConsoleAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.AddAppSettingsSecretsJson()
.ConfigureLogging((context, logging) => logging.ClearProviders())
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<DbMigratorHostedService>();
});
}

View File

@ -0,0 +1,16 @@
{
"ConnectionStrings": {
"Default": "Host=localhost;Port=5432;Database=Dispatch;User ID=root;Password=myPassword;"
},
"Redis": {
"Configuration": "127.0.0.1"
},
"OpenIddict": {
"Applications": {
"Dispatch_Swagger": {
"ClientId": "Dispatch_Swagger",
"RootUrl": "https://localhost:44321"
}
}
}
}

View File

@ -0,0 +1,6 @@
namespace KonSoft.Dispatch;
public static class DispatchDomainErrorCodes
{
/* You can add your business exception error codes here, as constants */
}

View File

@ -0,0 +1,58 @@
using KonSoft.Dispatch.Localization;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(AbpAuditLoggingDomainSharedModule),
typeof(AbpBackgroundJobsDomainSharedModule),
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpIdentityDomainSharedModule),
typeof(AbpOpenIddictDomainSharedModule),
typeof(AbpPermissionManagementDomainSharedModule),
typeof(AbpSettingManagementDomainSharedModule),
typeof(AbpTenantManagementDomainSharedModule)
)]
public class DispatchDomainSharedModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
DispatchGlobalFeatureConfigurator.Configure();
DispatchModuleExtensionConfigurator.Configure();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<DispatchDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<DispatchResource>("en")
.AddBaseTypes(typeof(AbpValidationResource))
.AddVirtualJson("/Localization/Dispatch");
options.DefaultResourceType = typeof(DispatchResource);
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace("Dispatch", typeof(DispatchResource));
});
}
}

View File

@ -0,0 +1,22 @@
using Volo.Abp.Threading;
namespace KonSoft.Dispatch;
public static class DispatchGlobalFeatureConfigurator
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
/* You can configure (enable/disable) global features of the used modules here.
*
* YOU CAN SAFELY DELETE THIS CLASS AND REMOVE ITS USAGES IF YOU DON'T NEED TO IT!
*
* Please refer to the documentation to lear more about the Global Features System:
* https://docs.abp.io/en/abp/latest/Global-Features
*/
});
}
}

View File

@ -0,0 +1,73 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace KonSoft.Dispatch;
public static class DispatchModuleExtensionConfigurator
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
ConfigureExistingProperties();
ConfigureExtraProperties();
});
}
private static void ConfigureExistingProperties()
{
/* You can change max lengths for properties of the
* entities defined in the modules used by your application.
*
* Example: Change user and role name max lengths
AbpUserConsts.MaxNameLength = 99;
IdentityRoleConsts.MaxNameLength = 99;
* Notice: It is not suggested to change property lengths
* unless you really need it. Go with the standard values wherever possible.
*
* If you are using EF Core, you will need to run the add-migration command after your changes.
*/
}
private static void ConfigureExtraProperties()
{
/* You can configure extra properties for the
* entities defined in the modules used by your application.
*
* This class can be used to define these extra properties
* with a high level, easy to use API.
*
* Example: Add a new property to the user entity of the identity module
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>( //property type: string
"SocialSecurityNumber", //property name
property =>
{
//validation rules
property.Attributes.Add(new RequiredAttribute());
property.Attributes.Add(new StringLengthAttribute(64) {MinimumLength = 4});
property.Configuration[IdentityModuleExtensionConsts.ConfigurationNames.AllowUserToEdit] = true;
//...other configurations for this property
}
);
});
});
* See the documentation for more:
* https://docs.abp.io/en/abp/latest/Module-Entity-Extensions
*/
}
}

View File

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Identity.Domain.Shared" Version="8.3.4" />
<PackageReference Include="Volo.Abp.BackgroundJobs.Domain.Shared" Version="8.3.4" />
<PackageReference Include="Volo.Abp.AuditLogging.Domain.Shared" Version="8.3.4" />
<PackageReference Include="Volo.Abp.TenantManagement.Domain.Shared" Version="8.3.4" />
<PackageReference Include="Volo.Abp.FeatureManagement.Domain.Shared" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Shared" Version="8.3.4" />
<PackageReference Include="Volo.Abp.SettingManagement.Domain.Shared" Version="8.3.4" />
<PackageReference Include="Volo.Abp.OpenIddict.Domain.Shared" Version="8.3.4" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\Dispatch\*.json" />
<Content Remove="Localization\Dispatch\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="8.0.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
{
"culture": "ar",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "الصفحة الرئيسية",
"Welcome": "مرحباً",
"LongWelcomeMessage": "مرحبا بكم في التطبيق. هذا مشروع بدء تشغيل يعتمد على إطار عمل ABP. لمزيد من المعلومات ، يرجى زيارة abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "cs",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Úvod",
"Welcome": "Vítejte",
"LongWelcomeMessage": "Vítejte v aplikaci. Toto je startovací projekt založený na ABP frameworku. Pro více informací, navštivte abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "de",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Home",
"Welcome": "Willkommen",
"LongWelcomeMessage": "Willkommen bei der Anwendung. Dies ist ein Startup-Projekt, das auf dem ABP-Framework basiert. Weitere Informationen finden Sie unter abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "en-GB",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "en",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "es",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Inicio",
"Welcome": "Bienvenido",
"LongWelcomeMessage": "Bienvenido a la aplicación, este es un proyecto base basado en el framework ABP. Para más información, visita abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "fi",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Koti",
"Welcome": "Tervetuloa",
"LongWelcomeMessage": "Tervetuloa sovellukseen. Tämä on ABP-kehykseen perustuva käynnistysprojekti. Lisätietoja on osoitteessa abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "fr",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Accueil",
"Welcome": "Bienvenue",
"LongWelcomeMessage": "Bienvenue dans l'application. Il s'agit d'un projet de démarrage basé sur le framework ABP. Pour plus d'informations, visitez abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "hi",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "घर",
"Welcome": "स्वागत हे",
"LongWelcomeMessage": "आवेदन करने के लिए आपका स्वागत है। यह एबीपी ढांचे पर आधारित एक स्टार्टअप परियोजना है। अधिक जानकारी के लिए, abp.io पर जाएं।"
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "hr",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Početna",
"Welcome": "Dobrodošli",
"LongWelcomeMessage": "Dobrodošli u aplikaciju. Ovo je startup projekt temeljen na ABP framework-u. Za više informacija posjetite abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "hu",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Kezdőlap",
"Welcome": "Üdvözlöm",
"LongWelcomeMessage": "Üdvözöljük az alkalmazásban. Ez egy ABP keretrendszeren alapuló startup projekt. További információkért látogasson el az abp.io oldalra."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "is",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Heim",
"Welcome": "Velkomin",
"LongWelcomeMessage": "Verið velkomin í forritið. Þetta er startup verkefni sem byggir á ABP. Nánari upplýsingar er að finna á abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "it",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Home",
"Welcome": "Benvenuto",
"LongWelcomeMessage": "Benvenuto nell'applicazione. Questo è un progetto di avvio basato sul framework ABP. Per ulteriori informazioni, visita abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "nl",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Home",
"Welcome": "Welkom",
"LongWelcomeMessage": "Welkom bij de applicatie. Dit is een startup-project gebaseerd op het ABP-framework. Bezoek abp.io voor meer informatie."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "pl-PL",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Home",
"Welcome": "Witaj",
"LongWelcomeMessage": "Witaj w aplikacji. To jest inicjalny projekt bazujący na ABP framework. Po więcej informacji odwiedź stronę abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "pt-BR",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Principal",
"Welcome": "Seja bem-vindo!",
"LongWelcomeMessage": "Bem-vindo a esta aplicação. Este é um projeto inicial baseado no ABP framework. Para mais informações, visite abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "ro-RO",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Acasă",
"Welcome": "Bun venit",
"LongWelcomeMessage": "Bun venit la aplicaţie. Acesta este un proiect de pornire bazat pe framework-ul ABP. Pentru mai multe informaţii, vizitaţi, visit abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "ru",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Главная",
"Welcome": "Добро пожаловать",
"LongWelcomeMessage": "Добро пожаловать в приложение. Этот запущенный проект основан на фреймворке ABP. Для получения дополнительной информации посетите сайт abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "sk",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Domov",
"Welcome": "Vitajte",
"LongWelcomeMessage": "Vitajte v aplikácii. Toto je štartovací projekt založený na ABP frameworku. Viac informácií nájdete na stránke abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "sl",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Domov",
"Welcome": "Dobrodošli",
"LongWelcomeMessage": "Dobrodošli v aplikaciji. To je začetni projekt na osnovi okolja ABP. Za več informacij obiščite abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "sv",
"texts": {
"AppName": "MittProjektNamn",
"Menu:Home": "Hem",
"Welcome": "Välkommen",
"LongWelcomeMessage": "Välkommen till applikationen. Detta är ett startup-projekt baserat på ABP-ramverket. För mer information, besök abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "tr",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Ana sayfa",
"Welcome": "Hoşgeldiniz",
"LongWelcomeMessage": "Uygulamaya hoşgeldiniz. Bu, ABP framework'ü üzerine bina edilmiş bir başlangıç projesidir. Daha fazla bilgi için abp.io adresini ziyaret edebilirsiniz."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "vi",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "Trang chủ",
"Welcome": "Chào mừng bạn",
"LongWelcomeMessage": "Chào mừng bạn đến ứng dụng. Đây là một dự án khởi nghiệp dựa trên khung ABP. Để biết thêm thông tin, hãy truy cập abp.io."
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "zh-Hans",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "首页",
"Welcome": "欢迎",
"LongWelcomeMessage": "欢迎使用本应用程序。这是一个基于 ABP 框架的启动项目。更多信息,请访问 abp.io。"
}
}

View File

@ -0,0 +1,9 @@
{
"culture": "zh-Hant",
"texts": {
"AppName": "Dispatch",
"Menu:Home": "首頁",
"Welcome": "歡迎",
"LongWelcomeMessage": "歡迎來到此應用程式. 這是一個基於ABP框架的起始專案. 有關更多訊息, 請瀏覽 abp.io."
}
}

View File

@ -0,0 +1,9 @@
using Volo.Abp.Localization;
namespace KonSoft.Dispatch.Localization;
[LocalizationResourceName("Dispatch")]
public class DispatchResource
{
}

View File

@ -0,0 +1,10 @@
namespace KonSoft.Dispatch.MultiTenancy;
public static class MultiTenancyConsts
{
/* Enable/disable multi-tenancy easily in a single point.
* If you will never need to multi-tenancy, you can remove
* related modules and code parts, including this file.
*/
public const bool IsEnabled = true;
}

View File

@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.TenantManagement;
namespace KonSoft.Dispatch.Data;
public class DispatchDbMigrationService : ITransientDependency
{
public ILogger<DispatchDbMigrationService> Logger { get; set; }
private readonly IDataSeeder _dataSeeder;
private readonly IEnumerable<IDispatchDbSchemaMigrator> _dbSchemaMigrators;
private readonly ITenantRepository _tenantRepository;
private readonly ICurrentTenant _currentTenant;
public DispatchDbMigrationService(
IDataSeeder dataSeeder,
IEnumerable<IDispatchDbSchemaMigrator> dbSchemaMigrators,
ITenantRepository tenantRepository,
ICurrentTenant currentTenant)
{
_dataSeeder = dataSeeder;
_dbSchemaMigrators = dbSchemaMigrators;
_tenantRepository = tenantRepository;
_currentTenant = currentTenant;
Logger = NullLogger<DispatchDbMigrationService>.Instance;
}
public async Task MigrateAsync()
{
var initialMigrationAdded = AddInitialMigrationIfNotExist();
if (initialMigrationAdded)
{
return;
}
Logger.LogInformation("Started database migrations...");
await MigrateDatabaseSchemaAsync();
await SeedDataAsync();
Logger.LogInformation($"Successfully completed host database migrations.");
var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
var migratedDatabaseSchemas = new HashSet<string>();
foreach (var tenant in tenants)
{
using (_currentTenant.Change(tenant.Id))
{
if (tenant.ConnectionStrings.Any())
{
var tenantConnectionStrings = tenant.ConnectionStrings
.Select(x => x.Value)
.ToList();
if (!migratedDatabaseSchemas.IsSupersetOf(tenantConnectionStrings))
{
await MigrateDatabaseSchemaAsync(tenant);
migratedDatabaseSchemas.AddIfNotContains(tenantConnectionStrings);
}
}
await SeedDataAsync(tenant);
}
Logger.LogInformation($"Successfully completed {tenant.Name} tenant database migrations.");
}
Logger.LogInformation("Successfully completed all database migrations.");
Logger.LogInformation("You can safely end this process...");
}
private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null)
{
Logger.LogInformation(
$"Migrating schema for {(tenant == null ? "host" : tenant.Name + " tenant")} database...");
foreach (var migrator in _dbSchemaMigrators)
{
await migrator.MigrateAsync();
}
}
private async Task SeedDataAsync(Tenant? tenant = null)
{
Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed...");
await _dataSeeder.SeedAsync(new DataSeedContext(tenant?.Id)
.WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, IdentityDataSeedContributor.AdminEmailDefaultValue)
.WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, IdentityDataSeedContributor.AdminPasswordDefaultValue)
);
}
private bool AddInitialMigrationIfNotExist()
{
try
{
if (!DbMigrationsProjectExists())
{
return false;
}
}
catch (Exception)
{
return false;
}
try
{
if (!MigrationsFolderExists())
{
AddInitialMigration();
return true;
}
else
{
return false;
}
}
catch (Exception e)
{
Logger.LogWarning("Couldn't determinate if any migrations exist : " + e.Message);
return false;
}
}
private bool DbMigrationsProjectExists()
{
var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath();
return dbMigrationsProjectFolder != null;
}
private bool MigrationsFolderExists()
{
var dbMigrationsProjectFolder = GetEntityFrameworkCoreProjectFolderPath();
return dbMigrationsProjectFolder != null && Directory.Exists(Path.Combine(dbMigrationsProjectFolder, "Migrations"));
}
private void AddInitialMigration()
{
Logger.LogInformation("Creating initial migration...");
string argumentPrefix;
string fileName;
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
argumentPrefix = "-c";
fileName = "/bin/bash";
}
else
{
argumentPrefix = "/C";
fileName = "cmd.exe";
}
var procStartInfo = new ProcessStartInfo(fileName,
$"{argumentPrefix} \"abp create-migration-and-run-migrator \"{GetEntityFrameworkCoreProjectFolderPath()}\"\""
);
try
{
Process.Start(procStartInfo);
}
catch (Exception)
{
throw new Exception("Couldn't run ABP CLI...");
}
}
private string? GetEntityFrameworkCoreProjectFolderPath()
{
var slnDirectoryPath = GetSolutionDirectoryPath();
if (slnDirectoryPath == null)
{
throw new Exception("Solution folder not found!");
}
var srcDirectoryPath = Path.Combine(slnDirectoryPath, "src");
return Directory.GetDirectories(srcDirectoryPath)
.FirstOrDefault(d => d.EndsWith(".EntityFrameworkCore"));
}
private string? GetSolutionDirectoryPath()
{
var currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
while (currentDirectory != null && Directory.GetParent(currentDirectory.FullName) != null)
{
currentDirectory = Directory.GetParent(currentDirectory.FullName);
if (currentDirectory != null && Directory.GetFiles(currentDirectory.FullName).FirstOrDefault(f => f.EndsWith(".sln")) != null)
{
return currentDirectory.FullName;
}
}
return null;
}
}

View File

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace KonSoft.Dispatch.Data;
public interface IDispatchDbSchemaMigrator
{
Task MigrateAsync();
}

View File

@ -0,0 +1,15 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace KonSoft.Dispatch.Data;
/* This is used if database provider does't define
* IDispatchDbSchemaMigrator implementation.
*/
public class NullDispatchDbSchemaMigrator : IDispatchDbSchemaMigrator, ITransientDependency
{
public Task MigrateAsync()
{
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,8 @@
namespace KonSoft.Dispatch;
public static class DispatchConsts
{
public const string DbTablePrefix = "App";
public const string DbSchema = null;
}

View File

@ -0,0 +1,68 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using KonSoft.Dispatch.MultiTenancy;
using Volo.Abp.AuditLogging;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Emailing;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.PermissionManagement.OpenIddict;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(DispatchDomainSharedModule),
typeof(AbpAuditLoggingDomainModule),
typeof(AbpBackgroundJobsDomainModule),
typeof(AbpFeatureManagementDomainModule),
typeof(AbpIdentityDomainModule),
typeof(AbpOpenIddictDomainModule),
typeof(AbpPermissionManagementDomainOpenIddictModule),
typeof(AbpPermissionManagementDomainIdentityModule),
typeof(AbpSettingManagementDomainModule),
typeof(AbpTenantManagementDomainModule),
typeof(AbpEmailingModule)
)]
public class DispatchDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLocalizationOptions>(options =>
{
options.Languages.Add(new LanguageInfo("ar", "ar", "العربية"));
options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština"));
options.Languages.Add(new LanguageInfo("en", "en", "English"));
options.Languages.Add(new LanguageInfo("en-GB", "en-GB", "English (UK)"));
options.Languages.Add(new LanguageInfo("hu", "hu", "Magyar"));
options.Languages.Add(new LanguageInfo("hr", "hr", "Croatian"));
options.Languages.Add(new LanguageInfo("fi", "fi", "Finnish"));
options.Languages.Add(new LanguageInfo("fr", "fr", "Français"));
options.Languages.Add(new LanguageInfo("hi", "hi", "Hindi"));
options.Languages.Add(new LanguageInfo("it", "it", "Italiano"));
options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português"));
options.Languages.Add(new LanguageInfo("ru", "ru", "Русский"));
options.Languages.Add(new LanguageInfo("sk", "sk", "Slovak"));
options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "繁體中文"));
options.Languages.Add(new LanguageInfo("de-DE", "de-DE", "Deutsch"));
options.Languages.Add(new LanguageInfo("es", "es", "Español"));
});
Configure<AbpMultiTenancyOptions>(options =>
{
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
#if DEBUG
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
#endif
}
}

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\KonSoft.Dispatch.Domain.Shared\KonSoft.Dispatch.Domain.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Emailing" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Identity.Domain" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.Identity" Version="8.3.4" />
<PackageReference Include="Volo.Abp.BackgroundJobs.Domain" Version="8.3.4" />
<PackageReference Include="Volo.Abp.AuditLogging.Domain" Version="8.3.4" />
<PackageReference Include="Volo.Abp.TenantManagement.Domain" Version="8.3.4" />
<PackageReference Include="Volo.Abp.FeatureManagement.Domain" Version="8.3.4" />
<PackageReference Include="Volo.Abp.SettingManagement.Domain" Version="8.3.4" />
<PackageReference Include="Volo.Abp.OpenIddict.Domain" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.Domain.OpenIddict" Version="8.3.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,327 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using OpenIddict.Abstractions;
using Volo.Abp;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.OpenIddict.Applications;
using Volo.Abp.OpenIddict.Scopes;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Uow;
namespace KonSoft.Dispatch.OpenIddict;
/* Creates initial data that is needed to property run the application
* and make client-to-server communication possible.
*/
public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IConfiguration _configuration;
private readonly IOpenIddictApplicationRepository _openIddictApplicationRepository;
private readonly IAbpApplicationManager _applicationManager;
private readonly IOpenIddictScopeRepository _openIddictScopeRepository;
private readonly IOpenIddictScopeManager _scopeManager;
private readonly IPermissionDataSeeder _permissionDataSeeder;
private readonly IStringLocalizer<OpenIddictResponse> L;
public OpenIddictDataSeedContributor(
IConfiguration configuration,
IOpenIddictApplicationRepository openIddictApplicationRepository,
IAbpApplicationManager applicationManager,
IOpenIddictScopeRepository openIddictScopeRepository,
IOpenIddictScopeManager scopeManager,
IPermissionDataSeeder permissionDataSeeder,
IStringLocalizer<OpenIddictResponse> l )
{
_configuration = configuration;
_openIddictApplicationRepository = openIddictApplicationRepository;
_applicationManager = applicationManager;
_openIddictScopeRepository = openIddictScopeRepository;
_scopeManager = scopeManager;
_permissionDataSeeder = permissionDataSeeder;
L = l;
}
[UnitOfWork]
public virtual async Task SeedAsync(DataSeedContext context)
{
await CreateScopesAsync();
await CreateApplicationsAsync();
}
private async Task CreateScopesAsync()
{
if (await _openIddictScopeRepository.FindByNameAsync("Dispatch") == null)
{
await _scopeManager.CreateAsync(new OpenIddictScopeDescriptor {
Name = "Dispatch", DisplayName = "Dispatch API", Resources = { "Dispatch" }
});
}
}
private async Task CreateApplicationsAsync()
{
var commonScopes = new List<string> {
OpenIddictConstants.Permissions.Scopes.Address,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Phone,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles,
"Dispatch"
};
var configurationSection = _configuration.GetSection("OpenIddict:Applications");
// Swagger Client
var swaggerClientId = configurationSection["Dispatch_Swagger:ClientId"];
if (!swaggerClientId.IsNullOrWhiteSpace())
{
var swaggerRootUrl = configurationSection["Dispatch_Swagger:RootUrl"]?.TrimEnd('/');
await CreateApplicationAsync(
name: swaggerClientId!,
type: OpenIddictConstants.ClientTypes.Public,
consentType: OpenIddictConstants.ConsentTypes.Implicit,
displayName: "Swagger Application",
secret: null,
grantTypes: new List<string> { OpenIddictConstants.GrantTypes.AuthorizationCode, },
scopes: commonScopes,
redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html",
clientUri: swaggerRootUrl
);
}
}
private async Task CreateApplicationAsync(
[NotNull] string name,
[NotNull] string type,
[NotNull] string consentType,
string displayName,
string? secret,
List<string> grantTypes,
List<string> scopes,
string? clientUri = null,
string? redirectUri = null,
string? postLogoutRedirectUri = null,
List<string>? permissions = null)
{
if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public,
StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException(L["NoClientSecretCanBeSetForPublicApplications"]);
}
if (string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Confidential,
StringComparison.OrdinalIgnoreCase))
{
throw new BusinessException(L["TheClientSecretIsRequiredForConfidentialApplications"]);
}
var client = await _openIddictApplicationRepository.FindByClientIdAsync(name);
var application = new AbpApplicationDescriptor {
ClientId = name,
ClientType = type,
ClientSecret = secret,
ConsentType = consentType,
DisplayName = displayName,
ClientUri = clientUri,
};
Check.NotNullOrEmpty(grantTypes, nameof(grantTypes));
Check.NotNullOrEmpty(scopes, nameof(scopes));
if (new[] { OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.Implicit }.All(
grantTypes.Contains))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken);
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdTokenToken);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeToken);
}
}
if (!redirectUri.IsNullOrWhiteSpace() || !postLogoutRedirectUri.IsNullOrWhiteSpace())
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Logout);
}
var buildInGrantTypes = new[] {
OpenIddictConstants.GrantTypes.Implicit, OpenIddictConstants.GrantTypes.Password,
OpenIddictConstants.GrantTypes.AuthorizationCode, OpenIddictConstants.GrantTypes.ClientCredentials,
OpenIddictConstants.GrantTypes.DeviceCode, OpenIddictConstants.GrantTypes.RefreshToken
};
foreach (var grantType in grantTypes)
{
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Code);
}
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Authorization);
}
if (grantType == OpenIddictConstants.GrantTypes.AuthorizationCode ||
grantType == OpenIddictConstants.GrantTypes.ClientCredentials ||
grantType == OpenIddictConstants.GrantTypes.Password ||
grantType == OpenIddictConstants.GrantTypes.RefreshToken ||
grantType == OpenIddictConstants.GrantTypes.DeviceCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Token);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
}
if (grantType == OpenIddictConstants.GrantTypes.ClientCredentials)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.ClientCredentials);
}
if (grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Implicit);
}
if (grantType == OpenIddictConstants.GrantTypes.Password)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.Password);
}
if (grantType == OpenIddictConstants.GrantTypes.RefreshToken)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.RefreshToken);
}
if (grantType == OpenIddictConstants.GrantTypes.DeviceCode)
{
application.Permissions.Add(OpenIddictConstants.Permissions.GrantTypes.DeviceCode);
application.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Device);
}
if (grantType == OpenIddictConstants.GrantTypes.Implicit)
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdToken);
if (string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken);
application.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.Token);
}
}
if (!buildInGrantTypes.Contains(grantType))
{
application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.GrantType + grantType);
}
}
var buildInScopes = new[] {
OpenIddictConstants.Permissions.Scopes.Address, OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Phone, OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Scopes.Roles
};
foreach (var scope in scopes)
{
if (buildInScopes.Contains(scope))
{
application.Permissions.Add(scope);
}
else
{
application.Permissions.Add(OpenIddictConstants.Permissions.Prefixes.Scope + scope);
}
}
if (redirectUri != null)
{
if (!redirectUri.IsNullOrEmpty())
{
if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri) || !uri.IsWellFormedOriginalString())
{
throw new BusinessException(L["InvalidRedirectUri", redirectUri]);
}
if (application.RedirectUris.All(x => x != uri))
{
application.RedirectUris.Add(uri);
}
}
}
if (postLogoutRedirectUri != null)
{
if (!postLogoutRedirectUri.IsNullOrEmpty())
{
if (!Uri.TryCreate(postLogoutRedirectUri, UriKind.Absolute, out var uri) ||
!uri.IsWellFormedOriginalString())
{
throw new BusinessException(L["InvalidPostLogoutRedirectUri", postLogoutRedirectUri]);
}
if (application.PostLogoutRedirectUris.All(x => x != uri))
{
application.PostLogoutRedirectUris.Add(uri);
}
}
}
if (permissions != null)
{
await _permissionDataSeeder.SeedAsync(
ClientPermissionValueProvider.ProviderName,
name,
permissions,
null
);
}
if (client == null)
{
await _applicationManager.CreateAsync(application);
return;
}
if (!HasSameRedirectUris(client, application))
{
client.RedirectUris = JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/')));
client.PostLogoutRedirectUris = JsonSerializer.Serialize(application.PostLogoutRedirectUris.Select(q => q.ToString().TrimEnd('/')));
await _applicationManager.UpdateAsync(client.ToModel());
}
if (!HasSameScopes(client, application))
{
client.Permissions = JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString()));
await _applicationManager.UpdateAsync(client.ToModel());
}
}
private bool HasSameRedirectUris(OpenIddictApplication existingClient, AbpApplicationDescriptor application)
{
return existingClient.RedirectUris == JsonSerializer.Serialize(application.RedirectUris.Select(q => q.ToString().TrimEnd('/')));
}
private bool HasSameScopes(OpenIddictApplication existingClient, AbpApplicationDescriptor application)
{
return existingClient.Permissions == JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString().TrimEnd('/')));
}
}

View File

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleToAttribute("KonSoft.Dispatch.Domain.Tests")]
[assembly:InternalsVisibleToAttribute("KonSoft.Dispatch.TestBase")]

View File

@ -0,0 +1,12 @@
using Volo.Abp.Settings;
namespace KonSoft.Dispatch.Settings;
public class DispatchSettingDefinitionProvider : SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
//Define your own settings here. Example:
//context.Add(new SettingDefinition(DispatchSettings.MySetting1));
}
}

View File

@ -0,0 +1,9 @@
namespace KonSoft.Dispatch.Settings;
public static class DispatchSettings
{
private const string Prefix = "Dispatch";
//Add your own setting names here. Example:
//public const string MySetting1 = Prefix + ".MySetting1";
}

View File

@ -0,0 +1,86 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace KonSoft.Dispatch.EntityFrameworkCore;
[ReplaceDbContext(typeof(IIdentityDbContext))]
[ReplaceDbContext(typeof(ITenantManagementDbContext))]
[ConnectionStringName("Default")]
public class DispatchDbContext :
AbpDbContext<DispatchDbContext>,
IIdentityDbContext,
ITenantManagementDbContext
{
/* Add DbSet properties for your Aggregate Roots / Entities here. */
#region Entities from the modules
/* Notice: We only implemented IIdentityDbContext and ITenantManagementDbContext
* and replaced them for this DbContext. This allows you to perform JOIN
* queries for the entities of these modules over the repositories easily. You
* typically don't need that for other modules. But, if you need, you can
* implement the DbContext interface of the needed module and use ReplaceDbContext
* attribute just like IIdentityDbContext and ITenantManagementDbContext.
*
* More info: Replacing a DbContext of a module ensures that the related module
* uses this DbContext on runtime. Otherwise, it will use its own DbContext class.
*/
//Identity
public DbSet<IdentityUser> Users { get; set; }
public DbSet<IdentityRole> Roles { get; set; }
public DbSet<IdentityClaimType> ClaimTypes { get; set; }
public DbSet<OrganizationUnit> OrganizationUnits { get; set; }
public DbSet<IdentitySecurityLog> SecurityLogs { get; set; }
public DbSet<IdentityLinkUser> LinkUsers { get; set; }
public DbSet<IdentityUserDelegation> UserDelegations { get; set; }
public DbSet<IdentitySession> Sessions { get; set; }
// Tenant Management
public DbSet<Tenant> Tenants { get; set; }
public DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }
#endregion
public DispatchDbContext(DbContextOptions<DispatchDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Include modules to your migration db context */
builder.ConfigurePermissionManagement();
builder.ConfigureSettingManagement();
builder.ConfigureBackgroundJobs();
builder.ConfigureAuditLogging();
builder.ConfigureIdentity();
builder.ConfigureOpenIddict();
builder.ConfigureFeatureManagement();
builder.ConfigureTenantManagement();
/* Configure your own tables/entities inside here */
//builder.Entity<YourEntity>(b =>
//{
// b.ToTable(DispatchConsts.DbTablePrefix + "YourEntities", DispatchConsts.DbSchema);
// b.ConfigureByConvention(); //auto configure for the base class props
// //...
//});
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace KonSoft.Dispatch.EntityFrameworkCore;
/* This class is needed for EF Core console commands
* (like Add-Migration and Update-Database commands) */
public class DispatchDbContextFactory : IDesignTimeDbContextFactory<DispatchDbContext>
{
public DispatchDbContext CreateDbContext(string[] args)
{
// https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
DispatchEfCoreEntityExtensionMappings.Configure();
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<DispatchDbContext>()
.UseNpgsql(configuration.GetConnectionString("Default"));
return new DispatchDbContext(builder.Options);
}
private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../KonSoft.Dispatch.DbMigrator/"))
.AddJsonFile("appsettings.json", optional: false);
return builder.Build();
}
}

View File

@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace KonSoft.Dispatch.EntityFrameworkCore;
public static class DispatchEfCoreEntityExtensionMappings
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
DispatchGlobalFeatureConfigurator.Configure();
DispatchModuleExtensionConfigurator.Configure();
OneTimeRunner.Run(() =>
{
/* You can configure extra properties for the
* entities defined in the modules used by your application.
*
* This class can be used to map these extra properties to table fields in the database.
*
* USE THIS CLASS ONLY TO CONFIGURE EF CORE RELATED MAPPING.
* USE DispatchModuleExtensionConfigurator CLASS (in the Domain.Shared project)
* FOR A HIGH LEVEL API TO DEFINE EXTRA PROPERTIES TO ENTITIES OF THE USED MODULES
*
* Example: Map a property to a table field:
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
"MyProperty",
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(128);
}
);
* See the documentation for more:
* https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities
*/
});
}
}

View File

@ -0,0 +1,57 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Uow;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;
namespace KonSoft.Dispatch.EntityFrameworkCore;
[DependsOn(
typeof(DispatchDomainModule),
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpOpenIddictEntityFrameworkCoreModule),
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpSettingManagementEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCorePostgreSqlModule),
typeof(AbpBackgroundJobsEntityFrameworkCoreModule),
typeof(AbpAuditLoggingEntityFrameworkCoreModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(AbpFeatureManagementEntityFrameworkCoreModule)
)]
public class DispatchEntityFrameworkCoreModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
// https://www.npgsql.org/efcore/release-notes/6.0.html#opting-out-of-the-new-timestamp-mapping-logic
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
DispatchEfCoreEntityExtensionMappings.Configure();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<DispatchDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories(includeAllEntities: true);
});
Configure<AbpDbContextOptions>(options =>
{
/* The main point to change your DBMS.
* See also DispatchMigrationsDbContextFactory for EF Core tooling. */
options.UseNpgsql();
});
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using KonSoft.Dispatch.Data;
using Volo.Abp.DependencyInjection;
namespace KonSoft.Dispatch.EntityFrameworkCore;
public class EntityFrameworkCoreDispatchDbSchemaMigrator
: IDispatchDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreDispatchDbSchemaMigrator(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrateAsync()
{
/* We intentionally resolve the DispatchDbContext
* from IServiceProvider (instead of directly injecting it)
* to properly get the connection string of the current tenant in the
* current scope.
*/
await _serviceProvider
.GetRequiredService<DispatchDbContext>()
.Database
.MigrateAsync();
}
}

View File

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\KonSoft.Dispatch.Domain\KonSoft.Dispatch.Domain.csproj" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.PostgreSql" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="8.3.4" />
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Identity.EntityFrameworkCore" Version="8.3.4" />
<PackageReference Include="Volo.Abp.BackgroundJobs.EntityFrameworkCore" Version="8.3.4" />
<PackageReference Include="Volo.Abp.AuditLogging.EntityFrameworkCore" Version="8.3.4" />
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="8.3.4" />
<PackageReference Include="Volo.Abp.FeatureManagement.EntityFrameworkCore" Version="8.3.4" />
<PackageReference Include="Volo.Abp.OpenIddict.EntityFrameworkCore" Version="8.3.4" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleToAttribute("KonSoft.Dispatch.EntityFrameworkCore.Tests")]

View File

@ -0,0 +1,38 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Account;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.TenantManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.VirtualFileSystem;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(DispatchApplicationContractsModule),
typeof(AbpAccountHttpApiClientModule),
typeof(AbpIdentityHttpApiClientModule),
typeof(AbpPermissionManagementHttpApiClientModule),
typeof(AbpTenantManagementHttpApiClientModule),
typeof(AbpFeatureManagementHttpApiClientModule),
typeof(AbpSettingManagementHttpApiClientModule)
)]
public class DispatchHttpApiClientModule : AbpModule
{
public const string RemoteServiceName = "Default";
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(
typeof(DispatchApplicationContractsModule).Assembly,
RemoteServiceName
);
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<DispatchHttpApiClientModule>();
});
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\KonSoft.Dispatch.Application.Contracts\KonSoft.Dispatch.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Account.HttpApi.Client" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Identity.HttpApi.Client" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.HttpApi.Client" Version="8.3.4" />
<PackageReference Include="Volo.Abp.TenantManagement.HttpApi.Client" Version="8.3.4" />
<PackageReference Include="Volo.Abp.FeatureManagement.HttpApi.Client" Version="8.3.4" />
<PackageReference Include="Volo.Abp.SettingManagement.HttpApi.Client" Version="8.3.4" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="**\*generate-proxy.json" />
<Content Remove="**\*generate-proxy.json" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,14 @@
using KonSoft.Dispatch.Localization;
using Volo.Abp.AspNetCore.Mvc;
namespace KonSoft.Dispatch.Controllers;
/* Inherit your controllers from this class.
*/
public abstract class DispatchController : AbpControllerBase
{
protected DispatchController()
{
LocalizationResource = typeof(DispatchResource);
}
}

View File

@ -0,0 +1,41 @@
using Localization.Resources.AbpUi;
using KonSoft.Dispatch.Localization;
using Volo.Abp.Account;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.HttpApi;
using Volo.Abp.SettingManagement;
using Volo.Abp.TenantManagement;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(DispatchApplicationContractsModule),
typeof(AbpAccountHttpApiModule),
typeof(AbpIdentityHttpApiModule),
typeof(AbpPermissionManagementHttpApiModule),
typeof(AbpTenantManagementHttpApiModule),
typeof(AbpFeatureManagementHttpApiModule),
typeof(AbpSettingManagementHttpApiModule)
)]
public class DispatchHttpApiModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
ConfigureLocalization();
}
private void ConfigureLocalization()
{
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<DispatchResource>()
.AddBaseTypes(
typeof(AbpUiResource)
);
});
}
}

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\KonSoft.Dispatch.Application.Contracts\KonSoft.Dispatch.Application.Contracts.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Account.HttpApi" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Identity.HttpApi" Version="8.3.4" />
<PackageReference Include="Volo.Abp.PermissionManagement.HttpApi" Version="8.3.4" />
<PackageReference Include="Volo.Abp.TenantManagement.HttpApi" Version="8.3.4" />
<PackageReference Include="Volo.Abp.FeatureManagement.HttpApi" Version="8.3.4" />
<PackageReference Include="Volo.Abp.SettingManagement.HttpApi" Version="8.3.4" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,10 @@
using System;
namespace KonSoft.Dispatch.Models.Test;
public class TestModel
{
public string? Name { get; set; }
public DateTime BirthDate { get; set; }
}

View File

@ -0,0 +1,9 @@
using Volo.Abp.Modularity;
namespace KonSoft.Dispatch;
public abstract class DispatchApplicationTestBase<TStartupModule> : DispatchTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
}

View File

@ -0,0 +1,12 @@
using Volo.Abp.Modularity;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(DispatchApplicationModule),
typeof(DispatchDomainTestModule)
)]
public class DispatchApplicationTestModule : AbpModule
{
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\KonSoft.Dispatch.Application\KonSoft.Dispatch.Application.csproj" />
<ProjectReference Include="..\KonSoft.Dispatch.Domain.Tests\KonSoft.Dispatch.Domain.Tests.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,34 @@
using Shouldly;
using System.Threading.Tasks;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Xunit;
namespace KonSoft.Dispatch.Samples;
/* This is just an example test class.
* Normally, you don't test code of the modules you are using
* (like IIdentityUserAppService here).
* Only test your own application services.
*/
public abstract class SampleAppServiceTests<TStartupModule> : DispatchApplicationTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
private readonly IIdentityUserAppService _userAppService;
protected SampleAppServiceTests()
{
_userAppService = GetRequiredService<IIdentityUserAppService>();
}
[Fact]
public async Task Initial_Data_Should_Contain_Admin_User()
{
//Act
var result = await _userAppService.GetListAsync(new GetIdentityUsersInput());
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(u => u.UserName == "admin");
}
}

View File

@ -0,0 +1,10 @@
using Volo.Abp.Modularity;
namespace KonSoft.Dispatch;
/* Inherit from this class for your domain layer tests. */
public abstract class DispatchDomainTestBase<TStartupModule> : DispatchTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
}

View File

@ -0,0 +1,12 @@
using Volo.Abp.Modularity;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(DispatchDomainModule),
typeof(DispatchTestBaseModule)
)]
public class DispatchDomainTestModule : AbpModule
{
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\KonSoft.Dispatch.Domain\KonSoft.Dispatch.Domain.csproj" />
<ProjectReference Include="..\KonSoft.Dispatch.TestBase\KonSoft.Dispatch.TestBase.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,46 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Xunit;
namespace KonSoft.Dispatch.Samples;
/* This is just an example test class.
* Normally, you don't test code of the modules you are using
* (like IdentityUserManager here).
* Only test your own domain services.
*/
public abstract class SampleDomainTests<TStartupModule> : DispatchDomainTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
private readonly IIdentityUserRepository _identityUserRepository;
private readonly IdentityUserManager _identityUserManager;
protected SampleDomainTests()
{
_identityUserRepository = GetRequiredService<IIdentityUserRepository>();
_identityUserManager = GetRequiredService<IdentityUserManager>();
}
[Fact]
public async Task Should_Set_Email_Of_A_User()
{
IdentityUser adminUser;
/* Need to manually start Unit Of Work because
* FirstOrDefaultAsync should be executed while db connection / context is available.
*/
await WithUnitOfWorkAsync(async () =>
{
adminUser = await _identityUserRepository
.FindByNormalizedUserNameAsync("ADMIN");
await _identityUserManager.SetEmailAsync(adminUser, "newemail@abp.io");
await _identityUserRepository.UpdateAsync(adminUser);
});
adminUser = await _identityUserRepository.FindByNormalizedUserNameAsync("ADMIN");
adminUser.Email.ShouldBe("newemail@abp.io");
}
}

View File

@ -0,0 +1,10 @@
using KonSoft.Dispatch.Samples;
using Xunit;
namespace KonSoft.Dispatch.EntityFrameworkCore.Applications;
[Collection(DispatchTestConsts.CollectionDefinitionName)]
public class EfCoreSampleAppServiceTests : SampleAppServiceTests<DispatchEntityFrameworkCoreTestModule>
{
}

View File

@ -0,0 +1,9 @@
using Xunit;
namespace KonSoft.Dispatch.EntityFrameworkCore;
[CollectionDefinition(DispatchTestConsts.CollectionDefinitionName)]
public class DispatchEntityFrameworkCoreCollection : ICollectionFixture<DispatchEntityFrameworkCoreFixture>
{
}

View File

@ -0,0 +1,9 @@
using KonSoft.Dispatch.EntityFrameworkCore;
using Xunit;
namespace KonSoft.Dispatch.EntityFrameworkCore;
public class DispatchEntityFrameworkCoreCollectionFixtureBase : ICollectionFixture<DispatchEntityFrameworkCoreFixture>
{
}

View File

@ -0,0 +1,11 @@
using System;
namespace KonSoft.Dispatch.EntityFrameworkCore;
public class DispatchEntityFrameworkCoreFixture : IDisposable
{
public void Dispose()
{
}
}

View File

@ -0,0 +1,8 @@
using Volo.Abp;
namespace KonSoft.Dispatch.EntityFrameworkCore;
public abstract class DispatchEntityFrameworkCoreTestBase : DispatchTestBase<DispatchEntityFrameworkCoreTestModule>
{
}

View File

@ -0,0 +1,82 @@
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
using Volo.Abp.SettingManagement;
using Volo.Abp.Uow;
namespace KonSoft.Dispatch.EntityFrameworkCore;
[DependsOn(
typeof(DispatchApplicationTestModule),
typeof(DispatchEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class DispatchEntityFrameworkCoreTestModule : AbpModule
{
private SqliteConnection? _sqliteConnection;
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<FeatureManagementOptions>(options =>
{
options.SaveStaticFeaturesToDatabase = false;
options.IsDynamicFeatureStoreEnabled = false;
});
Configure<PermissionManagementOptions>(options =>
{
options.SaveStaticPermissionsToDatabase = false;
options.IsDynamicPermissionStoreEnabled = false;
});
Configure<SettingManagementOptions>(options =>
{
options.SaveStaticSettingsToDatabase = false;
options.IsDynamicSettingStoreEnabled = false;
});
context.Services.AddAlwaysDisableUnitOfWorkTransaction();
ConfigureInMemorySqlite(context.Services);
}
private void ConfigureInMemorySqlite(IServiceCollection services)
{
_sqliteConnection = CreateDatabaseAndGetConnection();
services.Configure<AbpDbContextOptions>(options =>
{
options.Configure(context =>
{
context.DbContextOptions.UseSqlite(_sqliteConnection);
});
});
}
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
_sqliteConnection?.Dispose();
}
private static SqliteConnection CreateDatabaseAndGetConnection()
{
var connection = new AbpUnitTestSqliteConnection("Data Source=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<DispatchDbContext>()
.UseSqlite(connection)
.Options;
using (var context = new DispatchDbContext(options))
{
context.GetService<IRelationalDatabaseCreator>().CreateTables();
}
return connection;
}
}

View File

@ -0,0 +1,10 @@
using KonSoft.Dispatch.Samples;
using Xunit;
namespace KonSoft.Dispatch.EntityFrameworkCore.Domains;
[Collection(DispatchTestConsts.CollectionDefinitionName)]
public class EfCoreSampleDomainTests : SampleDomainTests<DispatchEntityFrameworkCoreTestModule>
{
}

View File

@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore;
using Shouldly;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Xunit;
namespace KonSoft.Dispatch.EntityFrameworkCore.Samples;
/* This is just an example test class.
* Normally, you don't test ABP framework code
* (like default AppUser repository IRepository<AppUser, Guid> here).
* Only test your custom repository methods.
*/
[Collection(DispatchTestConsts.CollectionDefinitionName)]
public class SampleRepositoryTests : DispatchEntityFrameworkCoreTestBase
{
private readonly IRepository<IdentityUser, Guid> _appUserRepository;
public SampleRepositoryTests()
{
_appUserRepository = GetRequiredService<IRepository<IdentityUser, Guid>>();
}
[Fact]
public async Task Should_Query_AppUser()
{
/* Need to manually start Unit Of Work because
* FirstOrDefaultAsync should be executed while db connection / context is available.
*/
await WithUnitOfWorkAsync(async () =>
{
//Act
var adminUser = await (await _appUserRepository.GetQueryableAsync())
.Where(u => u.UserName == "admin")
.FirstOrDefaultAsync();
//Assert
adminUser.ShouldNotBeNull();
});
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<RootNamespace>KonSoft.Dispatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\KonSoft.Dispatch.EntityFrameworkCore\KonSoft.Dispatch.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\KonSoft.Dispatch.Application.Tests\KonSoft.Dispatch.Application.Tests.csproj" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.Sqlite" Version="8.3.4" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Account;
using Volo.Abp.DependencyInjection;
namespace KonSoft.Dispatch.HttpApi.Client.ConsoleTestApp;
public class ClientDemoService : ITransientDependency
{
private readonly IProfileAppService _profileAppService;
public ClientDemoService(IProfileAppService profileAppService)
{
_profileAppService = profileAppService;
}
public async Task RunAsync()
{
var output = await _profileAppService.GetAsync();
Console.WriteLine($"UserName : {output.UserName}");
Console.WriteLine($"Email : {output.Email}");
Console.WriteLine($"Name : {output.Name}");
Console.WriteLine($"Surname : {output.Surname}");
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
namespace KonSoft.Dispatch.HttpApi.Client.ConsoleTestApp;
public class ConsoleTestAppHostedService : IHostedService
{
private readonly IConfiguration _configuration;
public ConsoleTestAppHostedService(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var application = await AbpApplicationFactory.CreateAsync<DispatchConsoleApiClientModule>(options =>
{
options.Services.ReplaceConfiguration(_configuration);
options.UseAutofac();
}))
{
await application.InitializeAsync();
var demo = application.ServiceProvider.GetRequiredService<ClientDemoService>();
await demo.RunAsync();
await application.ShutdownAsync();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

View File

@ -0,0 +1,30 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Polly;
using Volo.Abp.Autofac;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.IdentityModel;
using Volo.Abp.Modularity;
namespace KonSoft.Dispatch.HttpApi.Client.ConsoleTestApp;
[DependsOn(
typeof(AbpAutofacModule),
typeof(DispatchHttpApiClientModule),
typeof(AbpHttpClientIdentityModelModule)
)]
public class DispatchConsoleApiClientModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpHttpClientBuilderOptions>(options =>
{
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>
{
clientBuilder.AddTransientHttpErrorPolicy(
policyBuilder => policyBuilder.WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(Math.Pow(2, i)))
);
});
});
}
}

View File

@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Remove="appsettings.secrets.json" />
<Content Include="appsettings.secrets.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Http.Client.IdentityModel" Version="8.3.4" />
<PackageReference Include="Volo.Abp.Autofac" Version="8.3.4" />
<ProjectReference Include="..\..\src\KonSoft.Dispatch.HttpApi.Client\KonSoft.Dispatch.HttpApi.Client.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,22 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace KonSoft.Dispatch.HttpApi.Client.ConsoleTestApp;
class Program
{
static async Task Main(string[] args)
{
await CreateHostBuilder(args).RunConsoleAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.AddAppSettingsSecretsJson()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<ConsoleTestAppHostedService>();
});
}

View File

@ -0,0 +1,17 @@
{
"RemoteServices": {
"Default": {
"BaseUrl": "https://localhost:44321"
}
},
"IdentityClients": {
"Default": {
"GrantType": "password",
"ClientId": "Dispatch_App",
"UserName": "admin",
"UserPassword": "1q2w3E*",
"Authority": "https://localhost:44321",
"Scope": "Dispatch"
}
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.Uow;
using Volo.Abp.Testing;
namespace KonSoft.Dispatch;
/* All test classes are derived from this class, directly or indirectly.
*/
public abstract class DispatchTestBase<TStartupModule> : AbpIntegratedTest<TStartupModule>
where TStartupModule : IAbpModule
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
protected virtual Task WithUnitOfWorkAsync(Func<Task> func)
{
return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
}
protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func<Task> action)
{
using (var scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
using (var uow = uowManager.Begin(options))
{
await action();
await uow.CompleteAsync();
}
}
}
protected virtual Task<TResult> WithUnitOfWorkAsync<TResult>(Func<Task<TResult>> func)
{
return WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
}
protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(AbpUnitOfWorkOptions options, Func<Task<TResult>> func)
{
using (var scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
using (var uow = uowManager.Begin(options))
{
var result = await func();
await uow.CompleteAsync();
return result;
}
}
}
}

View File

@ -0,0 +1,47 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Authorization;
using Volo.Abp.Autofac;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Data;
using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace KonSoft.Dispatch;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpTestBaseModule),
typeof(AbpAuthorizationModule),
typeof(AbpBackgroundJobsAbstractionsModule)
)]
public class DispatchTestBaseModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBackgroundJobOptions>(options =>
{
options.IsJobExecutionEnabled = false;
});
context.Services.AddAlwaysAllowAuthorization();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
SeedTestData(context);
}
private static void SeedTestData(ApplicationInitializationContext context)
{
AsyncHelper.RunSync(async () =>
{
using (var scope = context.ServiceProvider.CreateScope())
{
await scope.ServiceProvider
.GetRequiredService<IDataSeeder>()
.SeedAsync();
}
});
}
}

Some files were not shown because too many files have changed in this diff Show More