diff --git a/modules/tenantmanagement/README.md b/modules/tenantmanagement/README.md new file mode 100644 index 0000000000..060f0d2136 --- /dev/null +++ b/modules/tenantmanagement/README.md @@ -0,0 +1,2 @@ +# abp-tenantmanagement +Tenant management module for ABP framework. diff --git a/modules/tenantmanagement/Volo.Abp.TenantManagement.sln b/modules/tenantmanagement/Volo.Abp.TenantManagement.sln new file mode 100644 index 0000000000..20fd21a531 --- /dev/null +++ b/modules/tenantmanagement/Volo.Abp.TenantManagement.sln @@ -0,0 +1,116 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{799CA525-4748-421A-9892-05C68BB2FA13}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C6941869-A9FC-4BEA-AD3F-C1E104826ECA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.Domain.Shared", "src\Volo.Abp.TenantManagement.Domain.Shared\Volo.Abp.TenantManagement.Domain.Shared.csproj", "{44BBBCA1-2E6A-419E-87E6-A04468E887D1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.Domain", "src\Volo.Abp.TenantManagement.Domain\Volo.Abp.TenantManagement.Domain.csproj", "{FBFB1FD6-10B5-4416-BFD1-21A62754AD33}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.Application.Contracts", "src\Volo.Abp.TenantManagement.Application.Contracts\Volo.Abp.TenantManagement.Application.Contracts.csproj", "{0C93BFF5-8B86-4CE7-86D8-893B0C44192F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.Application", "src\Volo.Abp.TenantManagement.Application\Volo.Abp.TenantManagement.Application.csproj", "{57E7A9DA-4149-4CE4-B9DC-75A064A9648B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.EntityFrameworkCore", "src\Volo.Abp.TenantManagement.EntityFrameworkCore\Volo.Abp.TenantManagement.EntityFrameworkCore.csproj", "{1BCA75A2-10DB-4200-A006-7C003767F0D9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.HttpApi", "src\Volo.Abp.TenantManagement.HttpApi\Volo.Abp.TenantManagement.HttpApi.csproj", "{996C1AB4-1F0A-4D6A-BB30-21245C7112D0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.HttpApi.Client", "src\Volo.Abp.TenantManagement.HttpApi.Client\Volo.Abp.TenantManagement.HttpApi.Client.csproj", "{3B042AC5-29F1-4037-BF17-4025092FCB7A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.Web", "src\Volo.Abp.TenantManagement.Web\Volo.Abp.TenantManagement.Web.csproj", "{2AE28C0A-1B8A-4D78-A9A1-7F2271A6F2D2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.Application.Tests", "test\Volo.Abp.TenantManagement.Application.Tests\Volo.Abp.TenantManagement.Application.Tests.csproj", "{72445B2D-07FA-4A35-A3D6-FF4ACE299BF4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TenantManagement.EntityFrameworkCore.Tests", "test\Volo.Abp.TenantManagement.EntityFrameworkCore.Tests\Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj", "{A2BB8897-EBDB-46BB-B885-F8635B21F376}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TenantManagement.MongoDB", "src\Volo.Abp.TenantManagement.MongoDB\Volo.Abp.TenantManagement.MongoDB.csproj", "{ED95242E-3C31-4A89-9C62-93B306EFEB15}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TenantManagement.MongoDB.Tests", "test\Volo.Abp.TenantManagement.MongoDB.Tests\Volo.Abp.TenantManagement.MongoDB.Tests.csproj", "{F75B4C54-A5F1-4101-99F5-A5B868A5146B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.TenantManagement.TestBase", "test\Volo.Abp.TenantManagement.TestBase\Volo.Abp.TenantManagement.TestBase.csproj", "{C3BAD6E8-00CD-4283-9416-64287BB5B265}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {44BBBCA1-2E6A-419E-87E6-A04468E887D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44BBBCA1-2E6A-419E-87E6-A04468E887D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44BBBCA1-2E6A-419E-87E6-A04468E887D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44BBBCA1-2E6A-419E-87E6-A04468E887D1}.Release|Any CPU.Build.0 = Release|Any CPU + {FBFB1FD6-10B5-4416-BFD1-21A62754AD33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBFB1FD6-10B5-4416-BFD1-21A62754AD33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBFB1FD6-10B5-4416-BFD1-21A62754AD33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBFB1FD6-10B5-4416-BFD1-21A62754AD33}.Release|Any CPU.Build.0 = Release|Any CPU + {0C93BFF5-8B86-4CE7-86D8-893B0C44192F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C93BFF5-8B86-4CE7-86D8-893B0C44192F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C93BFF5-8B86-4CE7-86D8-893B0C44192F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C93BFF5-8B86-4CE7-86D8-893B0C44192F}.Release|Any CPU.Build.0 = Release|Any CPU + {57E7A9DA-4149-4CE4-B9DC-75A064A9648B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57E7A9DA-4149-4CE4-B9DC-75A064A9648B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57E7A9DA-4149-4CE4-B9DC-75A064A9648B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57E7A9DA-4149-4CE4-B9DC-75A064A9648B}.Release|Any CPU.Build.0 = Release|Any CPU + {1BCA75A2-10DB-4200-A006-7C003767F0D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BCA75A2-10DB-4200-A006-7C003767F0D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BCA75A2-10DB-4200-A006-7C003767F0D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BCA75A2-10DB-4200-A006-7C003767F0D9}.Release|Any CPU.Build.0 = Release|Any CPU + {996C1AB4-1F0A-4D6A-BB30-21245C7112D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {996C1AB4-1F0A-4D6A-BB30-21245C7112D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {996C1AB4-1F0A-4D6A-BB30-21245C7112D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {996C1AB4-1F0A-4D6A-BB30-21245C7112D0}.Release|Any CPU.Build.0 = Release|Any CPU + {3B042AC5-29F1-4037-BF17-4025092FCB7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B042AC5-29F1-4037-BF17-4025092FCB7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B042AC5-29F1-4037-BF17-4025092FCB7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B042AC5-29F1-4037-BF17-4025092FCB7A}.Release|Any CPU.Build.0 = Release|Any CPU + {2AE28C0A-1B8A-4D78-A9A1-7F2271A6F2D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AE28C0A-1B8A-4D78-A9A1-7F2271A6F2D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AE28C0A-1B8A-4D78-A9A1-7F2271A6F2D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AE28C0A-1B8A-4D78-A9A1-7F2271A6F2D2}.Release|Any CPU.Build.0 = Release|Any CPU + {72445B2D-07FA-4A35-A3D6-FF4ACE299BF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72445B2D-07FA-4A35-A3D6-FF4ACE299BF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72445B2D-07FA-4A35-A3D6-FF4ACE299BF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72445B2D-07FA-4A35-A3D6-FF4ACE299BF4}.Release|Any CPU.Build.0 = Release|Any CPU + {A2BB8897-EBDB-46BB-B885-F8635B21F376}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2BB8897-EBDB-46BB-B885-F8635B21F376}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2BB8897-EBDB-46BB-B885-F8635B21F376}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2BB8897-EBDB-46BB-B885-F8635B21F376}.Release|Any CPU.Build.0 = Release|Any CPU + {ED95242E-3C31-4A89-9C62-93B306EFEB15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED95242E-3C31-4A89-9C62-93B306EFEB15}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED95242E-3C31-4A89-9C62-93B306EFEB15}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED95242E-3C31-4A89-9C62-93B306EFEB15}.Release|Any CPU.Build.0 = Release|Any CPU + {F75B4C54-A5F1-4101-99F5-A5B868A5146B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F75B4C54-A5F1-4101-99F5-A5B868A5146B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F75B4C54-A5F1-4101-99F5-A5B868A5146B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F75B4C54-A5F1-4101-99F5-A5B868A5146B}.Release|Any CPU.Build.0 = Release|Any CPU + {C3BAD6E8-00CD-4283-9416-64287BB5B265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3BAD6E8-00CD-4283-9416-64287BB5B265}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3BAD6E8-00CD-4283-9416-64287BB5B265}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3BAD6E8-00CD-4283-9416-64287BB5B265}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {44BBBCA1-2E6A-419E-87E6-A04468E887D1} = {799CA525-4748-421A-9892-05C68BB2FA13} + {FBFB1FD6-10B5-4416-BFD1-21A62754AD33} = {799CA525-4748-421A-9892-05C68BB2FA13} + {0C93BFF5-8B86-4CE7-86D8-893B0C44192F} = {799CA525-4748-421A-9892-05C68BB2FA13} + {57E7A9DA-4149-4CE4-B9DC-75A064A9648B} = {799CA525-4748-421A-9892-05C68BB2FA13} + {1BCA75A2-10DB-4200-A006-7C003767F0D9} = {799CA525-4748-421A-9892-05C68BB2FA13} + {996C1AB4-1F0A-4D6A-BB30-21245C7112D0} = {799CA525-4748-421A-9892-05C68BB2FA13} + {3B042AC5-29F1-4037-BF17-4025092FCB7A} = {799CA525-4748-421A-9892-05C68BB2FA13} + {2AE28C0A-1B8A-4D78-A9A1-7F2271A6F2D2} = {799CA525-4748-421A-9892-05C68BB2FA13} + {72445B2D-07FA-4A35-A3D6-FF4ACE299BF4} = {C6941869-A9FC-4BEA-AD3F-C1E104826ECA} + {A2BB8897-EBDB-46BB-B885-F8635B21F376} = {C6941869-A9FC-4BEA-AD3F-C1E104826ECA} + {ED95242E-3C31-4A89-9C62-93B306EFEB15} = {799CA525-4748-421A-9892-05C68BB2FA13} + {F75B4C54-A5F1-4101-99F5-A5B868A5146B} = {C6941869-A9FC-4BEA-AD3F-C1E104826ECA} + {C3BAD6E8-00CD-4283-9416-64287BB5B265} = {C6941869-A9FC-4BEA-AD3F-C1E104826ECA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7C258726-2CE0-44D3-A2D7-71812E8F505C} + EndGlobalSection +EndGlobal diff --git a/modules/tenantmanagement/common.props b/modules/tenantmanagement/common.props new file mode 100644 index 0000000000..f00a3fc2cb --- /dev/null +++ b/modules/tenantmanagement/common.props @@ -0,0 +1,16 @@ + + + latest + 0.3.0 + $(NoWarn);CS1591 + http://www.aspnetboilerplate.com/images/abp_nupkg.png + http://abp.io + git + https://github.com/volosoft/abp/ + + + + + + + \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo.Abp.TenantManagement.Application.Contracts.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo.Abp.TenantManagement.Application.Contracts.csproj new file mode 100644 index 0000000000..b2e6b38a56 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo.Abp.TenantManagement.Application.Contracts.csproj @@ -0,0 +1,26 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.Application.Contracts + Volo.Abp.TenantManagement.Application.Contracts + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementApplicationContractsModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementApplicationContractsModule.cs new file mode 100644 index 0000000000..a049d96f92 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementApplicationContractsModule.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Application; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.TenantManagement.Localization; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.TenantManagement +{ + [DependsOn(typeof(AbpDddApplicationModule))] + [DependsOn(typeof(AbpTenantManagementDomainSharedModule))] + public class AbpTenantManagementApplicationContractsModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.DefinitionProviders.Add(); + }); + + services.Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + services.Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/Volo/Abp/TenantManagement/Localization/ApplicationContracts"); + }); + + services.AddAssemblyOf(); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementPermissionDefinitionProvider.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementPermissionDefinitionProvider.cs new file mode 100644 index 0000000000..822de19ad8 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementPermissionDefinitionProvider.cs @@ -0,0 +1,24 @@ +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; +using Volo.Abp.TenantManagement.Localization; + +namespace Volo.Abp.TenantManagement +{ + public class AbpTenantManagementPermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var identityGroup = context.AddGroup(TenantManagementPermissions.GroupName, L("Permission:TenantManagement")); + + var rolesPermission = identityGroup.AddPermission(TenantManagementPermissions.Tenants.Default, L("Permission:TenantManagement")); + rolesPermission.AddChild(TenantManagementPermissions.Tenants.Create, L("Permission:Create")); + rolesPermission.AddChild(TenantManagementPermissions.Tenants.Update, L("Permission:Edit")); + rolesPermission.AddChild(TenantManagementPermissions.Tenants.Delete, L("Permission:Delete")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/GetTenantsInput.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/GetTenantsInput.cs new file mode 100644 index 0000000000..b7bdc16e6c --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/GetTenantsInput.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Dtos; + +namespace Volo.Abp.TenantManagement +{ + public class GetTenantsInput : PagedAndSortedResultRequestDto + { + public string Filter { get; set; } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/ITenantAppService.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/ITenantAppService.cs new file mode 100644 index 0000000000..3f6df9b6fe --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/ITenantAppService.cs @@ -0,0 +1,10 @@ +using System; +using Volo.Abp.Application.Services; + +namespace Volo.Abp.TenantManagement +{ + public interface ITenantAppService : IAsyncCrudAppService + { + //TODO: Manage connection strings + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json new file mode 100644 index 0000000000..92a555e5a7 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json @@ -0,0 +1,9 @@ +{ + "culture": "en", + "texts": { + "Permission:TenantManagement": "Tenant management", + "Permission:Create": "Create", + "Permission:Edit": "Edit", + "Permission:Delete": "Delete" + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/tr.json b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/tr.json new file mode 100644 index 0000000000..a192048e78 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/tr.json @@ -0,0 +1,9 @@ +{ + "culture": "tr", + "texts": { + "Permission:TenantManagement": "Müşteri yönetimi", + "Permission:Create": "Oluşturma", + "Permission:Edit": "Düzenleme", + "Permission:Delete": "Silme" + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs new file mode 100644 index 0000000000..7561835ee3 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateDto.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.TenantManagement +{ + public class TenantCreateDto : TenantCreateOrUpdateDtoBase + { + + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs new file mode 100644 index 0000000000..a5a9b4af3d --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantCreateOrUpdateDtoBase.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.TenantManagement +{ + public abstract class TenantCreateOrUpdateDtoBase + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantDto.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantDto.cs new file mode 100644 index 0000000000..a410e90da5 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantDto.cs @@ -0,0 +1,10 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Volo.Abp.TenantManagement +{ + public class TenantDto : EntityDto + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs new file mode 100644 index 0000000000..bc30e65e1f --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.TenantManagement +{ + public static class TenantManagementPermissions + { + public const string GroupName = "AbpTenantManagement"; + + public static class Tenants + { + public const string Default = GroupName + ".Tenants"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantUpdateDto.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantUpdateDto.cs new file mode 100644 index 0000000000..8bbe7adebb --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantUpdateDto.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.TenantManagement +{ + public class TenantUpdateDto : TenantCreateOrUpdateDtoBase + { + + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo.Abp.TenantManagement.Application.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo.Abp.TenantManagement.Application.csproj new file mode 100644 index 0000000000..8a53a6b892 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo.Abp.TenantManagement.Application.csproj @@ -0,0 +1,23 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.Application + Volo.Abp.TenantManagement.Application + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationAutoMapperProfile.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationAutoMapperProfile.cs new file mode 100644 index 0000000000..61a2994dd7 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationAutoMapperProfile.cs @@ -0,0 +1,12 @@ +using AutoMapper; + +namespace Volo.Abp.TenantManagement +{ + public class AbpTenantManagementApplicationAutoMapperProfile : Profile + { + public AbpTenantManagementApplicationAutoMapperProfile() + { + CreateMap(); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationModule.cs new file mode 100644 index 0000000000..2cf0213119 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationModule.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Modularity; + +namespace Volo.Abp.TenantManagement +{ + [DependsOn(typeof(AbpTenantManagementDomainModule))] + [DependsOn(typeof(AbpTenantManagementApplicationContractsModule))] + public class AbpTenantManagementApplicationModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.AddProfile(validate: true); + }); + + services.AddAssemblyOf(); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs new file mode 100644 index 0000000000..8578d29090 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; + +namespace Volo.Abp.TenantManagement +{ + [Authorize(TenantManagementPermissions.Tenants.Default)] + public class TenantAppService : TenantManagementAppServiceBase, ITenantAppService + { + private readonly ITenantRepository _tenantRepository; + private readonly ITenantManager _tenantManager; + + public TenantAppService(ITenantRepository tenantRepository, ITenantManager tenantManager) + { + _tenantRepository = tenantRepository; + _tenantManager = tenantManager; + } + + public async Task GetAsync(Guid id) + { + return ObjectMapper.Map( + await _tenantRepository.GetAsync(id) + ); + } + + public async Task> GetListAsync(GetTenantsInput input) + { + var count = await _tenantRepository.GetCountAsync(); + var list = await _tenantRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, input.Filter); + + return new PagedResultDto( + count, + ObjectMapper.Map, List>(list) + ); + } + + [Authorize(TenantManagementPermissions.Tenants.Create)] + public async Task CreateAsync(TenantCreateDto input) + { + var tenant = await _tenantManager.CreateAsync(input.Name); + await _tenantRepository.InsertAsync(tenant); + return ObjectMapper.Map(tenant); + } + + [Authorize(TenantManagementPermissions.Tenants.Update)] + public async Task UpdateAsync(Guid id, TenantUpdateDto input) + { + var tenant = await _tenantRepository.GetAsync(id); + await _tenantManager.ChangeNameAsync(tenant, input.Name); + await _tenantRepository.UpdateAsync(tenant); + return ObjectMapper.Map(tenant); + } + + [Authorize(TenantManagementPermissions.Tenants.Delete)] + public async Task DeleteAsync(Guid id) + { + var tenant = await _tenantRepository.FindAsync(id); + if (tenant == null) + { + return; + } + + await _tenantRepository.DeleteAsync(tenant); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantManagementAppServiceBase.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantManagementAppServiceBase.cs new file mode 100644 index 0000000000..bd68ec9f39 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantManagementAppServiceBase.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Services; + +namespace Volo.Abp.TenantManagement +{ + public class TenantManagementAppServiceBase : ApplicationService + { + + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo.Abp.TenantManagement.Domain.Shared.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo.Abp.TenantManagement.Domain.Shared.csproj new file mode 100644 index 0000000000..2c5c210d8c --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo.Abp.TenantManagement.Domain.Shared.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.Domain.Shared + Volo.Abp.TenantManagement.Domain.Shared + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/AbpTenantManagementDomainSharedModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/AbpTenantManagementDomainSharedModule.cs new file mode 100644 index 0000000000..3ac78b5030 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/AbpTenantManagementDomainSharedModule.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.TenantManagement.Localization; + +namespace Volo.Abp.TenantManagement +{ + public class AbpTenantManagementDomainSharedModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.Resources.Add("en"); + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/AbpTenantManagementResource.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/AbpTenantManagementResource.cs new file mode 100644 index 0000000000..ef94d769b8 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/AbpTenantManagementResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Volo.Abp.TenantManagement.Localization +{ + [LocalizationResourceName("AbpTenantManagement")] + public class AbpTenantManagementResource + { + + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs new file mode 100644 index 0000000000..526d12b7d6 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConnectionStringConsts.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.TenantManagement +{ + public static class TenantConnectionStringConsts + { + public const int MaxNameLength = 128; + + public const int MaxValueLength = 1024; + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs new file mode 100644 index 0000000000..328c422ab9 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/TenantConsts.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.TenantManagement +{ + public static class TenantConsts + { + public const int MaxNameLength = 64; + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj new file mode 100644 index 0000000000..b3050edfa4 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj @@ -0,0 +1,26 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.Domain + Volo.Abp.TenantManagement.Domain + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj.DotSettings b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementConsts.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementConsts.cs new file mode 100644 index 0000000000..6f87227627 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementConsts.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.TenantManagement +{ + public static class AbpTenantManagementConsts + { + public const string DefaultDbTablePrefix = "Abp"; + + public const string DefaultDbSchema = null; + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainMappingProfile.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainMappingProfile.cs new file mode 100644 index 0000000000..ac928efda6 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainMappingProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Volo.Abp.Data; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.TenantManagement +{ + public class AbpTenantManagementDomainMappingProfile : Profile + { + public AbpTenantManagementDomainMappingProfile() + { + CreateMap() + .ForMember(ti => ti.ConnectionStrings, opts => + { + opts.ResolveUsing(tenant => + { + var connStrings = new ConnectionStrings(); + + foreach (var connectionString in tenant.ConnectionStrings) + { + connStrings[connectionString.Name] = connectionString.Value; + } + + return connStrings; + }); + }); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs new file mode 100644 index 0000000000..209fa47c79 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AutoMapper; +using Volo.Abp.Data; +using Volo.Abp.Domain; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.UI; + +namespace Volo.Abp.TenantManagement +{ + [DependsOn(typeof(AbpMultiTenancyAbstractionsModule))] + [DependsOn(typeof(AbpTenantManagementDomainSharedModule))] + [DependsOn(typeof(AbpDataModule))] + [DependsOn(typeof(AbpDddDomainModule))] + [DependsOn(typeof(AbpAutoMapperModule))] + [DependsOn(typeof(AbpUiModule))] //TODO: It's not good to depend on the UI module. However, UserFriendlyException is inside it! + public class AbpTenantManagementDomainModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.AddProfile(validate: true); + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantManager.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantManager.cs new file mode 100644 index 0000000000..5ab5443425 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantManager.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Domain.Services; + +namespace Volo.Abp.TenantManagement +{ + public interface ITenantManager : IDomainService + { + [NotNull] + Task CreateAsync([NotNull] string name); + + Task ChangeNameAsync([NotNull] Tenant tenant, [NotNull] string name); + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantRepository.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantRepository.cs new file mode 100644 index 0000000000..f7d430c214 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/ITenantRepository.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.TenantManagement +{ + public interface ITenantRepository : IBasicRepository + { + Task FindByNameAsync( + string name, + bool includeDetails = true, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + bool includeDetails = false, + CancellationToken cancellationToken = default); + + Task GetCountAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/Tenant.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/Tenant.cs new file mode 100644 index 0000000000..7c70746816 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/Tenant.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.TenantManagement +{ + public class Tenant : AggregateRoot, IHasExtraProperties + { + public virtual string Name { get; protected set; } + + public virtual List ConnectionStrings { get; protected set; } + + public Dictionary ExtraProperties { get; } + + protected Tenant() + { + ExtraProperties = new Dictionary(); + } + + protected internal Tenant(Guid id, [NotNull] string name) + { + Check.NotNull(name, nameof(name)); + + Id = id; + Name = name; + + ConnectionStrings = new List(); + ExtraProperties = new Dictionary(); + } + + [CanBeNull] + public virtual string FindDefaultConnectionString() + { + return FindConnectionString(Data.ConnectionStrings.DefaultConnectionStringName); + } + + [CanBeNull] + public virtual string FindConnectionString(string name) + { + return ConnectionStrings.FirstOrDefault(c => c.Name == name)?.Value; + } + + internal void SetName([NotNull] string name) + { + Check.NotNull(name, nameof(name)); + + Name = name; + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantConnectionString.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantConnectionString.cs new file mode 100644 index 0000000000..b555a0eea7 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantConnectionString.cs @@ -0,0 +1,30 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.TenantManagement +{ + public class TenantConnectionString : Entity + { + public virtual Guid TenantId { get; protected set; } + + public virtual string Name { get; protected set; } + + public virtual string Value { get; protected set; } + + protected TenantConnectionString() + { + + } + + public TenantConnectionString(Guid tenantId, [NotNull] string name, [NotNull] string value) + { + Check.NotNull(name, nameof(name)); + Check.NotNull(value, nameof(value)); + + TenantId = tenantId; + Name = name; + Value = value; + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs new file mode 100644 index 0000000000..b410632502 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantManager.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; +using Volo.Abp.UI; + +namespace Volo.Abp.TenantManagement +{ + public class TenantManager : DomainService, ITenantManager + { + private readonly ITenantRepository _tenantRepository; + + public TenantManager(ITenantRepository tenantRepository) + { + _tenantRepository = tenantRepository; + } + + public async Task CreateAsync(string name) + { + Check.NotNull(name, nameof(name)); + + await ValidateNameAsync(name); + return new Tenant(GuidGenerator.Create(), name); + } + + public async Task ChangeNameAsync(Tenant tenant, string name) + { + Check.NotNull(tenant, nameof(tenant)); + Check.NotNull(name, nameof(name)); + + await ValidateNameAsync(name, tenant.Id); + tenant.SetName(name); + } + + protected virtual async Task ValidateNameAsync(string name, Guid? expectedId = null) + { + var tenant = await _tenantRepository.FindByNameAsync(name); + if (tenant != null && tenant.Id != expectedId) + { + throw new UserFriendlyException("Duplicate tenancy name: " + name); //TODO: A domain exception would be better..? + } + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantStore.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantStore.cs new file mode 100644 index 0000000000..cd15ebe1fe --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantStore.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectMapping; + +namespace Volo.Abp.TenantManagement +{ + //TODO: This class should use caching instead of querying everytime! + + public class TenantStore : ITenantStore, ITransientDependency + { + private readonly ITenantRepository _tenantRepository; + private readonly IObjectMapper _objectMapper; + private readonly ICurrentTenant _currentTenant; + + public TenantStore( + ITenantRepository tenantRepository, + IObjectMapper objectMapper, + ICurrentTenant currentTenant) + { + _tenantRepository = tenantRepository; + _objectMapper = objectMapper; + _currentTenant = currentTenant; + } + + public async Task FindAsync(string name) + { + using (_currentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! + { + var tenant = await _tenantRepository.FindByNameAsync(name); + if (tenant == null) + { + return null; + } + + return _objectMapper.Map(tenant); + } + } + + public async Task FindAsync(Guid id) + { + using (_currentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! + { + var tenant = await _tenantRepository.FindAsync(id); + if (tenant == null) + { + return null; + } + + return _objectMapper.Map(tenant); + } + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo.Abp.TenantManagement.EntityFrameworkCore.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo.Abp.TenantManagement.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..35a3be7607 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo.Abp.TenantManagement.EntityFrameworkCore.csproj @@ -0,0 +1,22 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.EntityFrameworkCore + Volo.Abp.TenantManagement.EntityFrameworkCore + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo.Abp.TenantManagement.EntityFrameworkCore.csproj.DotSettings b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo.Abp.TenantManagement.EntityFrameworkCore.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo.Abp.TenantManagement.EntityFrameworkCore.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs new file mode 100644 index 0000000000..636692cf56 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs @@ -0,0 +1,42 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + public static class AbpTenantManagementDbContextModelCreatingExtensions + { + public static void ConfigureTenantManagement( + this ModelBuilder builder, + [CanBeNull] string tablePrefix = AbpTenantManagementConsts.DefaultDbTablePrefix, + [CanBeNull] string schema = AbpTenantManagementConsts.DefaultDbSchema) + { + Check.NotNull(builder, nameof(builder)); + + if (tablePrefix == null) + { + tablePrefix = ""; + } + + builder.Entity(b => + { + b.ToTable(tablePrefix + "Tenants", schema); + + b.Property(t => t.Name).IsRequired().HasMaxLength(TenantConsts.MaxNameLength); + + b.HasMany(u => u.ConnectionStrings).WithOne().HasForeignKey(uc => uc.TenantId).IsRequired(); + + b.HasIndex(u => u.Name).IsUnique(); + }); + + builder.Entity(b => + { + b.ToTable(tablePrefix + "TenantConnectionStrings", schema); + + b.HasKey(x => new { x.TenantId, x.Name }); + + b.Property(cs => cs.Name).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxNameLength); + b.Property(cs => cs.Value).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxValueLength); + }); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreModule.cs new file mode 100644 index 0000000000..809b1e805e --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreModule.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + [DependsOn(typeof(AbpTenantManagementDomainModule))] + [DependsOn(typeof(AbpEntityFrameworkCoreModule))] + public class AbpTenantManagementEntityFrameworkCoreModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAbpDbContext(options => + { + options.AddDefaultRepositories(); + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs new file mode 100644 index 0000000000..0a13f77bf0 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/EfCoreTenantRepository.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + public class EfCoreTenantRepository : EfCoreRepository, ITenantRepository + { + public EfCoreTenantRepository(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public virtual async Task FindByNameAsync( + string name, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .FirstOrDefaultAsync(t => t.Name == name, cancellationToken); + } + + public virtual async Task> GetListAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await DbSet + .IncludeDetails(includeDetails) + .WhereIf( + !filter.IsNullOrWhiteSpace(), + u => + u.Name.Contains(filter) + ) + .OrderBy(sorting ?? nameof(Tenant.Name)) + .PageBy(skipCount, maxResultCount) + .ToListAsync(cancellationToken); + } + + public virtual async Task GetCountAsync(CancellationToken cancellationToken = default) + { + return await DbSet.LongCountAsync(cancellationToken); + } + + public override IQueryable WithDetails() + { + return GetQueryable().IncludeDetails(); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/ITenantManagementDbContext.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/ITenantManagementDbContext.cs new file mode 100644 index 0000000000..fcd5174d0f --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/ITenantManagementDbContext.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + [ConnectionStringName("AbpTenantManagement")] + public interface ITenantManagementDbContext : IEfCoreDbContext + { + DbSet Tenants { get; set; } + + DbSet TenantConnectionStrings { get; set; } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantManagementDbContext.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantManagementDbContext.cs new file mode 100644 index 0000000000..382b6c1ab2 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantManagementDbContext.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + [ConnectionStringName("AbpTenantManagement")] + public class TenantManagementDbContext : AbpDbContext, ITenantManagementDbContext + { + public static string TablePrefix { get; set; } = AbpTenantManagementConsts.DefaultDbTablePrefix; + + public static string Schema { get; set; } = AbpTenantManagementConsts.DefaultDbSchema; + + public DbSet Tenants { get; set; } + + public DbSet TenantConnectionStrings { get; set; } + + public TenantManagementDbContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigureTenantManagement(TablePrefix, Schema); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantManagementEfCoreQueryableExtensions.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantManagementEfCoreQueryableExtensions.cs new file mode 100644 index 0000000000..3d40e2312c --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantManagementEfCoreQueryableExtensions.cs @@ -0,0 +1,19 @@ +using System.Linq; +using Microsoft.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + public static class TenantManagementEfCoreQueryableExtensions + { + public static IQueryable IncludeDetails(this IQueryable queryable, bool include = true) + { + if (!include) + { + return queryable; + } + + return queryable + .Include(x => x.ConnectionStrings); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi.Client/Volo.Abp.TenantManagement.HttpApi.Client.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi.Client/Volo.Abp.TenantManagement.HttpApi.Client.csproj new file mode 100644 index 0000000000..bd268f6740 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi.Client/Volo.Abp.TenantManagement.HttpApi.Client.csproj @@ -0,0 +1,22 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.HttpApi.Client + Volo.Abp.TenantManagement.HttpApi.Client + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi.Client/Volo/Abp/TenantManagement/AbpTenantManagementHttpApiClientModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi.Client/Volo/Abp/TenantManagement/AbpTenantManagementHttpApiClientModule.cs new file mode 100644 index 0000000000..fec7399b3a --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi.Client/Volo/Abp/TenantManagement/AbpTenantManagementHttpApiClientModule.cs @@ -0,0 +1,24 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; + +namespace Volo.Abp.TenantManagement +{ + [DependsOn( + typeof(AbpTenantManagementApplicationContractsModule), + typeof(AbpHttpClientModule))] + public class AbpTenantManagementHttpApiClientModule : AbpModule + { + public const string RemoteServiceName = "AbpTenantManagement"; + + public override void ConfigureServices(IServiceCollection services) + { + services.AddHttpClientProxies( + typeof(AbpTenantManagementApplicationContractsModule).Assembly, + RemoteServiceName + ); + + services.AddAssemblyOf(); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo.Abp.TenantManagement.HttpApi.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo.Abp.TenantManagement.HttpApi.csproj new file mode 100644 index 0000000000..b50780edf0 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo.Abp.TenantManagement.HttpApi.csproj @@ -0,0 +1,22 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.HttpApi + Volo.Abp.TenantManagement.HttpApi + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/AbpTenantManagementHttpApiModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/AbpTenantManagementHttpApiModule.cs new file mode 100644 index 0000000000..5e967e93f8 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/AbpTenantManagementHttpApiModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Modularity; + +namespace Volo.Abp.TenantManagement +{ + [DependsOn( + typeof(AbpTenantManagementApplicationContractsModule), + typeof(AbpAspNetCoreMvcModule) + )] + public class AbpTenantManagementHttpApiModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs new file mode 100644 index 0000000000..5091e4132a --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.HttpApi/Volo/Abp/TenantManagement/TenantController.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.TenantManagement +{ + [Controller] + [RemoteService] + [Area("multi-tenancy")] + public class TenantController : ITenantAppService, ITransientDependency //TODO: Throws exception on validation if we inherit from Controller + { + private readonly ITenantAppService _service; + + public TenantController(ITenantAppService service) + { + _service = service; + } + + public virtual Task GetAsync(Guid id) + { + return _service.GetAsync(id); + } + + public virtual Task> GetListAsync(GetTenantsInput input) + { + return _service.GetListAsync(input); + } + + public virtual Task CreateAsync(TenantCreateDto input) + { + return _service.CreateAsync(input); + } + + public virtual Task UpdateAsync(Guid id, TenantUpdateDto input) + { + return _service.UpdateAsync(id, input); + } + + public virtual Task DeleteAsync(Guid id) + { + return _service.DeleteAsync(id); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo.Abp.TenantManagement.MongoDB.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo.Abp.TenantManagement.MongoDB.csproj new file mode 100644 index 0000000000..f4de9566b2 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo.Abp.TenantManagement.MongoDB.csproj @@ -0,0 +1,22 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.MongoDB + Volo.Abp.TenantManagement.MongoDB + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo.Abp.TenantManagement.MongoDB.csproj.DotSettings b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo.Abp.TenantManagement.MongoDB.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo.Abp.TenantManagement.MongoDB.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementBsonClassMap.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementBsonClassMap.cs new file mode 100644 index 0000000000..f5a693bb65 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementBsonClassMap.cs @@ -0,0 +1,23 @@ +using MongoDB.Bson.Serialization; +using Volo.Abp.MongoDB; +using Volo.Abp.Threading; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + public static class AbpTenantManagementBsonClassMap + { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + public static void Configure() + { + OneTimeRunner.Run(() => + { + BsonClassMap.RegisterClassMap(map => + { + map.AutoMap(); + map.ConfigureExtraProperties(); + }); + }); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbContextExtensions.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbContextExtensions.cs new file mode 100644 index 0000000000..3b2a8f2a2a --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbContextExtensions.cs @@ -0,0 +1,24 @@ +using System; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + public static class AbpTenantManagementMongoDbContextExtensions + { + public static void ConfigureTenantManagement( + this IMongoModelBuilder builder, + Action optionsAction = null) + { + Check.NotNull(builder, nameof(builder)); + + var options = new TenantManagementMongoModelBuilderConfigurationOptions(); + + optionsAction?.Invoke(options); + + builder.Entity(b => + { + b.CollectionName = options.CollectionPrefix + "Tenants"; + }); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbModule.cs new file mode 100644 index 0000000000..b34d77d0f6 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbModule.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + [DependsOn( + typeof(AbpTenantManagementDomainModule), + typeof(AbpMongoDbModule) + )] + public class AbpTenantManagementMongoDbModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + AbpTenantManagementBsonClassMap.Configure(); + + services.AddMongoDbContext(options => + { + options.AddDefaultRepositories(); + + options.AddRepository(); + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/ITenantManagementMongoDbContext.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/ITenantManagementMongoDbContext.cs new file mode 100644 index 0000000000..0ee4336a47 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/ITenantManagementMongoDbContext.cs @@ -0,0 +1,12 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + [ConnectionStringName("AbpTenantManagement")] + public interface ITenantManagementMongoDbContext : IAbpMongoDbContext + { + IMongoCollection Tenants { get; } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/MongoTenantRepository.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/MongoTenantRepository.cs new file mode 100644 index 0000000000..05aa92757a --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/MongoTenantRepository.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver.Linq; +using System.Linq; +using System.Linq.Dynamic.Core; +using MongoDB.Driver; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + public class MongoTenantRepository : MongoDbRepository, ITenantRepository + { + public MongoTenantRepository(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public virtual async Task FindByNameAsync( + string name, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .FirstOrDefaultAsync(t => t.Name == name, cancellationToken); + } + + public virtual async Task GetCountAsync(CancellationToken cancellationToken = default) + { + return await GetMongoQueryable().LongCountAsync(cancellationToken); + } + + public virtual async Task> GetListAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + bool includeDetails = false, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .WhereIf>( + !filter.IsNullOrWhiteSpace(), + u => + u.Name.Contains(filter) + ) + .OrderBy(sorting ?? nameof(Tenant.Name)) + .As>() + .PageBy>(skipCount, maxResultCount) + .ToListAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/TenantManagementMongoDbContext.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/TenantManagementMongoDbContext.cs new file mode 100644 index 0000000000..4dc913c8d2 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/TenantManagementMongoDbContext.cs @@ -0,0 +1,24 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + [ConnectionStringName("AbpTenantManagement")] + public class TenantManagementMongoDbContext : AbpMongoDbContext, ITenantManagementMongoDbContext + { + public static string CollectionPrefix { get; set; } = AbpTenantManagementConsts.DefaultDbTablePrefix; + + public IMongoCollection Tenants => Collection(); + + protected override void CreateModel(IMongoModelBuilder modelBuilder) + { + base.CreateModel(modelBuilder); + + modelBuilder.ConfigureTenantManagement(options => + { + options.CollectionPrefix = CollectionPrefix; + }); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/TenantManagementMongoModelBuilderConfigurationOptions.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/TenantManagementMongoModelBuilderConfigurationOptions.cs new file mode 100644 index 0000000000..646a58bfb0 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.MongoDB/Volo/Abp/TenantManagement/MongoDb/TenantManagementMongoModelBuilderConfigurationOptions.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + public class TenantManagementMongoModelBuilderConfigurationOptions : MongoModelBuilderConfigurationOptions + { + public TenantManagementMongoModelBuilderConfigurationOptions([NotNull] string tablePrefix = AbpTenantManagementConsts.DefaultDbTablePrefix) + : base(tablePrefix) + { + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebAutoMapperProfile.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebAutoMapperProfile.cs new file mode 100644 index 0000000000..a37ed86dd0 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebAutoMapperProfile.cs @@ -0,0 +1,25 @@ +using AutoMapper; +using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants; + +namespace Volo.Abp.TenantManagement.Web +{ + public class AbpTenantManagementWebAutoMapperProfile : Profile + { + public AbpTenantManagementWebAutoMapperProfile() + { + CreateRoleMappings(); + } + + private void CreateRoleMappings() + { + //List + CreateMap(); + + //CreateModal + CreateMap(); + + //EditModal + CreateMap(); + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs new file mode 100644 index 0000000000..d5913f4bf0 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs @@ -0,0 +1,67 @@ +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; +using Volo.Abp.AutoMapper; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpValidation; +using Volo.Abp.Modularity; +using Volo.Abp.TenantManagement.Localization; +using Volo.Abp.TenantManagement.Web.Navigation; +using Volo.Abp.UI.Navigation; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.TenantManagement.Web +{ + [DependsOn(typeof(AbpTenantManagementHttpApiModule))] + [DependsOn(typeof(AbpAspNetCoreMvcUiBootstrapModule))] + [DependsOn(typeof(AbpAutoMapperModule))] + public class AbpTenantManagementWebModule : AbpModule + { + public override void PreConfigureServices(IServiceCollection services) + { + services.PreConfigure(options => + { + options.AddAssemblyResource(typeof(AbpTenantManagementResource), typeof(AbpTenantManagementWebModule).Assembly); + }); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.MenuContributors.Add(new AbpTenantManagementWebMainMenuContributor()); + }); + + services.Configure(options => + { + options.FileSets.AddEmbedded("Volo.Abp.TenantManagement.Web"); + }); + + services.Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpValidationResource), + typeof(AbpUiResource) + ).AddVirtualJson("/Localization/Resources/AbpTenantManagement/Web"); + }); + + services.Configure(options => + { + options.AddProfile(validate: true); + }); + + services.Configure(options => + { + options.Conventions.AuthorizePage("/TenantManagement/Tenants/Index", TenantManagementPermissions.Tenants.Default); + options.Conventions.AuthorizePage("/TenantManagement/Tenants/CreateModal", TenantManagementPermissions.Tenants.Create); + options.Conventions.AuthorizePage("/TenantManagement/Tenants/EditModal", TenantManagementPermissions.Tenants.Update); + }); + + services.AddAssemblyOf(); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/en.json b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/en.json new file mode 100644 index 0000000000..c6eab9bf5c --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/en.json @@ -0,0 +1,10 @@ +{ + "culture": "en", + "texts": { + "Menu:TenantManagement": "Tenant management", + "Tenants": "Tenants", + "NewTenant": "New tenant", + "TenantName": "Tenant name", + "TenantDeletionConfirmationMessage": "Tenant '{0}' will be deleted. Do you confirm that?" + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/tr.json b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/tr.json new file mode 100644 index 0000000000..8aaafaa7b6 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/tr.json @@ -0,0 +1,10 @@ +{ + "culture": "tr", + "texts": { + "Menu:TenantManagement": "Müşteri yönetimi", + "Tenants": "Müşteriler", + "NewTenant": "Yeni müşteri", + "TenantName": "Müşteri adı", + "TenantDeletionConfirmationMessage": "'{0}' isimli müşteri silinecektir. Onaylıyor musunuz?" + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Navigation/AbpIdentityWebMainMenuContributor.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Navigation/AbpIdentityWebMainMenuContributor.cs new file mode 100644 index 0000000000..ff8dc7f1a6 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Navigation/AbpIdentityWebMainMenuContributor.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using Volo.Abp.TenantManagement.Localization; +using Volo.Abp.UI.Navigation; + +namespace Volo.Abp.TenantManagement.Web.Navigation +{ + public class AbpTenantManagementWebMainMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name != StandardMenus.Main) + { + return; + } + + var authorizationService = context.ServiceProvider.GetRequiredService(); + var l = context.ServiceProvider.GetRequiredService>(); + + var tenantManagementMenuItem = new ApplicationMenuItem("TenantManagement", l["Menu:TenantManagement"]); + context.Menu.AddItem(tenantManagementMenuItem); + + if (await authorizationService.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)) + { + tenantManagementMenuItem.AddItem(new ApplicationMenuItem("Tenants", l["Tenants"], url: "/TenantManagement/Tenants")); + } + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml new file mode 100644 index 0000000000..ecef85ac8c --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml @@ -0,0 +1,18 @@ +@page +@using Microsoft.Extensions.Localization +@using Volo.Abp.TenantManagement.Localization +@using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants +@model CreateModalModel +@inject IStringLocalizer L +@{ + Layout = null; +} +
+ + + + + + + +
\ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs new file mode 100644 index 0000000000..a4239d6739 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/CreateModal.cshtml.cs @@ -0,0 +1,38 @@ +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants +{ + public class CreateModalModel : AbpPageModel + { + [BindProperty] + public TenantInfoModel Tenant { get; set; } + + private readonly ITenantAppService _tenantAppService; + + public CreateModalModel(ITenantAppService tenantAppService) + { + _tenantAppService = tenantAppService; + } + + public async Task OnPostAsync() + { + ValidateModel(); + + var input = ObjectMapper.Map(Tenant); + await _tenantAppService.CreateAsync(input); + + return NoContent(); + } + + public class TenantInfoModel + { + [Required] + [StringLength(TenantConsts.MaxNameLength)] + [Display(Name = "TenantName")] + public string Name { get; set; } + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml new file mode 100644 index 0000000000..beee12208d --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml @@ -0,0 +1,19 @@ +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.TenantManagement.Localization +@using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants +@model EditModalModel +@inject IHtmlLocalizer L +@{ + Layout = null; +} +
+ + + + + + + + +
\ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs new file mode 100644 index 0000000000..1c805b75b0 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/EditModal.cshtml.cs @@ -0,0 +1,49 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants +{ + public class EditModalModel : AbpPageModel + { + [BindProperty] + public TenantInfoModel Tenant { get; set; } + + private readonly ITenantAppService _tenantAppService; + + public EditModalModel(ITenantAppService tenantAppService) + { + _tenantAppService = tenantAppService; + } + + public async Task OnGetAsync(Guid id) + { + Tenant = ObjectMapper.Map( + await _tenantAppService.GetAsync(id) + ); + } + + public async Task OnPostAsync() + { + ValidateModel(); + + var input = ObjectMapper.Map(Tenant); + await _tenantAppService.UpdateAsync(Tenant.Id, input); + + return NoContent(); + } + + public class TenantInfoModel + { + [HiddenInput] + public Guid Id { get; set; } + + [Required] + [StringLength(TenantConsts.MaxNameLength)] + [Display(Name = "TenantName")] + public string Name { get; set; } + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.cshtml b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.cshtml new file mode 100644 index 0000000000..ec7b51e291 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.cshtml @@ -0,0 +1,34 @@ +@page +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.TenantManagement.Localization +@using Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants +@model IndexModel +@inject IHtmlLocalizer L +@section styles { + +} +@section scripts { + +} + + + + +

@L["Tenants"]

+
+ + + +
+
+ + + + + + + + +
@L["Actions"]@L["TenantName"]
+
+
\ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.cshtml.cs b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.cshtml.cs new file mode 100644 index 0000000000..c297f3d010 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants +{ + public class IndexModel : PageModel + { + public void OnGet() + { + + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/_ViewImports.cshtml b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/_ViewImports.cshtml new file mode 100644 index 0000000000..d1ac64721f --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..dab1d2b972 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55673/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Volo.Abp.TenantManagement.Web": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:55676/" + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj new file mode 100644 index 0000000000..ee74333b31 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/Volo.Abp.TenantManagement.Web.csproj @@ -0,0 +1,27 @@ + + + + + + netstandard2.0 + Volo.Abp.TenantManagement.Web + Volo.Abp.TenantManagement.Web + true + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + Library + + + + + + + + + + + + + + + + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/compilerconfig.json b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/compilerconfig.json new file mode 100644 index 0000000000..3104320327 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/compilerconfig.json @@ -0,0 +1,6 @@ +[ + { + "outputFile": "wwwroot/modules/multi-tenancy/views/tenants/index.css", + "inputFile": "wwwroot/modules/multi-tenancy/views/tenants/index.less" + } +] \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/compilerconfig.json.defaults b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/compilerconfig.json.defaults new file mode 100644 index 0000000000..c75eb7d519 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/compilerconfig.json.defaults @@ -0,0 +1,49 @@ +{ + "compilers": { + "less": { + "autoPrefix": "", + "cssComb": "none", + "ieCompat": true, + "strictMath": false, + "strictUnits": false, + "relativeUrls": true, + "rootPath": "", + "sourceMapRoot": "", + "sourceMapBasePath": "", + "sourceMap": false + }, + "sass": { + "includePath": "", + "indentType": "space", + "indentWidth": 2, + "outputStyle": "nested", + "Precision": 5, + "relativeUrls": true, + "sourceMapRoot": "", + "sourceMap": false + }, + "stylus": { + "sourceMap": false + }, + "babel": { + "sourceMap": false + }, + "coffeescript": { + "bare": false, + "runtimeMode": "node", + "sourceMap": false + } + }, + "minifiers": { + "css": { + "enabled": true, + "termSemicolons": true, + "gzip": false + }, + "javascript": { + "enabled": true, + "termSemicolons": true, + "gzip": false + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.css b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.css new file mode 100644 index 0000000000..41fbd4eaf0 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.css @@ -0,0 +1,21 @@ +.dataTable { + width: 100% !important; + border-spacing: 0 !important; +} +.table td, +.table th { + padding: 8px 10px; +} +.dataTable tbody tr td button { + cursor: pointer; +} +.dataTable tbody tr td div.dropdown ul.dropdown-menu li { + cursor: pointer; + padding: 5px; +} +.dataTable tbody tr td div.dropdown ul.dropdown-menu li a { + display: block; +} +.dataTable tbody tr td div.dropdown ul.dropdown-menu li:hover { + background: #f4f5f8; +} \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.es5.js b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.es5.js new file mode 100644 index 0000000000..bc6107abbc --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.es5.js @@ -0,0 +1,6 @@ +'use strict'; + +$(function () { + $('#IdentityUsersTable').DataTable(); +}); + diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.es5.min.js b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.es5.min.js new file mode 100644 index 0000000000..9a2c19267e --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.es5.min.js @@ -0,0 +1 @@ +"use strict";$(function(){$("#IdentityUsersTable").DataTable()}); \ No newline at end of file diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.js b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.js new file mode 100644 index 0000000000..5e0628879c --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.js @@ -0,0 +1,79 @@ +(function () { + + var l = abp.localization.getResource('AbpTenantManagement'); + var _tenantAppService = volo.abp.tenantManagement.tenant; + + var _editModal = new abp.ModalManager(abp.appPath + 'TenantManagement/Tenants/EditModal'); + var _createModal = new abp.ModalManager(abp.appPath + 'TenantManagement/Tenants/CreateModal'); + + $(function () { + + var _$wrapper = $('#TenantsWrapper'); + var _$table = _$wrapper.find('table'); + + var _dataTable = _$table.DataTable({ + order: [[1, "asc"]], + ajax: abp.libs.datatables.createAjax(_tenantAppService.getList), + columnDefs: [ + { + //TODO: Can we eleminate targets, data, orderable, autoWidth, defaultContent fields or make these values default + targets: 0, + data: null, + orderable: false, + autoWidth: false, + defaultContent: '', + rowAction: { + text: ' ' + l('Actions') + ' ', //TODO: Add icon option and set text as only l('Actions') + items: + [ + { + //TODO: Allow to add icon + text: l('Edit'), + visible: function () { //TODO: Allow visible to be a boolean for simple cases + return true; + }, + action: function (data) { + _editModal.open({ + id: data.record.id + }); + } + }, + { + text: l('Delete'), + visible: function () { + return true; + }, + confirmMessage: function (data) { return l('TenantDeletionConfirmationMessage', data.record.name)}, + action: function (data) { + _tenantAppService + .delete(data.record.id) + .then(function () { + _dataTable.ajax.reload(); + }); + } + } + ] + } + }, + { + targets: 1, + data: "name" + } + ] + }); + + _createModal.onResult(function () { + _dataTable.ajax.reload(); + }); + + _editModal.onResult(function () { + _dataTable.ajax.reload(); + }); + + _$wrapper.find('button[name=CreateTenant]').click(function (e) { + e.preventDefault(); + _createModal.open(); + }); + }); + +})(); diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.less b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.less new file mode 100644 index 0000000000..21adf56097 --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.less @@ -0,0 +1,39 @@ +//TODO: This code is duplicated for other pages too. Unify them. +.dataTable { + width: 100% !important; + border-spacing: 0 !important; +} + +.table td, .table th { + padding: 8px 10px; +} + +.dataTable { + tbody { + tr { + td { + + button { + cursor: pointer; + } + + div.dropdown { + ul.dropdown-menu { + li { + cursor: pointer; + padding: 5px; + + a { + display: block; + } + } + + li:hover { + background: #f4f5f8; + } + } + } + } + } + } +} diff --git a/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.min.css b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.min.css new file mode 100644 index 0000000000..e70b9e330b --- /dev/null +++ b/modules/tenantmanagement/src/Volo.Abp.TenantManagement.Web/wwwroot/modules/multi-tenancy/views/tenants/index.min.css @@ -0,0 +1 @@ +.dataTable{width:100% !important;border-spacing:0 !important;}.table td,.table th{padding:8px 10px;}.dataTable tbody tr td button{cursor:pointer;}.dataTable tbody tr td div.dropdown ul.dropdown-menu li{cursor:pointer;padding:5px;}.dataTable tbody tr td div.dropdown ul.dropdown-menu li a{display:block;}.dataTable tbody tr td div.dropdown ul.dropdown-menu li:hover{background:#f4f5f8;} \ No newline at end of file diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo.Abp.TenantManagement.Application.Tests.csproj b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo.Abp.TenantManagement.Application.Tests.csproj new file mode 100644 index 0000000000..bb806e268d --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo.Abp.TenantManagement.Application.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp2.0 + Volo.Abp.TenantManagement.Application.Tests + Volo.Abp.TenantManagement.Application.Tests + true + false + false + false + + + + + + + + + + + + + diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/AbpTenantManagementApplicationTestBase.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/AbpTenantManagementApplicationTestBase.cs new file mode 100644 index 0000000000..29e88032ff --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/AbpTenantManagementApplicationTestBase.cs @@ -0,0 +1,24 @@ +using System; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement +{ + public abstract class AbpTenantManagementApplicationTestBase : TenantManagementTestBase + { + protected virtual void UsingDbContext(Action action) + { + using (var dbContext = GetRequiredService()) + { + action.Invoke(dbContext); + } + } + + protected virtual T UsingDbContext(Func action) + { + using (var dbContext = GetRequiredService()) + { + return action.Invoke(dbContext); + } + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/AbpTenantManagementApplicationTestModule.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/AbpTenantManagementApplicationTestModule.cs new file mode 100644 index 0000000000..c8a9d0978d --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/AbpTenantManagementApplicationTestModule.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement +{ + [DependsOn( + typeof(AbpTenantManagementApplicationModule), + typeof(AbpTenantManagementEntityFrameworkCoreTestModule))] + public class AbpTenantManagementApplicationTestModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAlwaysAllowPermissionChecker(); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs new file mode 100644 index 0000000000..3e44e7c8ea --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.Application.Tests/Volo/Abp/TenantManagement/TenantAppService_Tests.cs @@ -0,0 +1,113 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.UI; +using Xunit; + +namespace Volo.Abp.TenantManagement +{ + public class TenantAppService_Tests : AbpTenantManagementApplicationTestBase + { + private readonly ITenantAppService _tenantAppService; + + public TenantAppService_Tests() + { + _tenantAppService = GetRequiredService(); + } + + [Fact] + public async Task GetAsync() + { + var tenantInDb = UsingDbContext(dbContext => dbContext.Tenants.First()); + var tenant = await _tenantAppService.GetAsync(tenantInDb.Id); + tenant.Name.ShouldBe(tenantInDb.Name); + } + + [Fact] + public async Task GetListAsync() + { + var result = await _tenantAppService.GetListAsync(new GetTenantsInput()); + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldContain(t => t.Name == "acme"); + result.Items.ShouldContain(t => t.Name == "volosoft"); + } + + [Fact] + public async Task GetListAsync_Filtered() + { + var result = await _tenantAppService.GetListAsync(new GetTenantsInput { Filter = "volo" }); + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldNotContain(t => t.Name == "acme"); + result.Items.ShouldContain(t => t.Name == "volosoft"); + } + + [Fact] + public async Task GetListAsync_Sorted_Descending_By_Name() + { + var result = await _tenantAppService.GetListAsync(new GetTenantsInput { Sorting = "Name DESC" }); + result.TotalCount.ShouldBeGreaterThan(0); + var tenants = result.Items.ToList(); + + tenants.ShouldContain(t => t.Name == "acme"); + tenants.ShouldContain(t => t.Name == "volosoft"); + + tenants.FindIndex(t => t.Name == "acme").ShouldBeGreaterThan(tenants.FindIndex(t => t.Name == "volosoft")); + } + + [Fact] + public async Task CreateAsync() + { + var tenancyName = Guid.NewGuid().ToString("N").ToLowerInvariant(); + var tenant = await _tenantAppService.CreateAsync(new TenantCreateDto { Name = tenancyName }); + tenant.Name.ShouldBe(tenancyName); + tenant.Id.ShouldNotBe(default(Guid)); + } + + [Fact] + public async Task CreateAsync_Should_Not_Allow_Duplicate_Names() + { + await Assert.ThrowsAsync(async () => + { + await _tenantAppService.CreateAsync(new TenantCreateDto { Name = "acme" }); + }); + } + + [Fact] + public async Task UpdateAsync() + { + var acme = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Name == "acme")); + + var result = await _tenantAppService.UpdateAsync(acme.Id, new TenantUpdateDto { Name = "acme-renamed" }); + result.Id.ShouldBe(acme.Id); + result.Name.ShouldBe("acme-renamed"); + + var acmeUpdated = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Id == acme.Id)); + acmeUpdated.Name.ShouldBe("acme-renamed"); + } + + [Fact] + public async Task UpdateAsync_Should_Not_Allow_Duplicate_Names() + { + var acme = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Name == "acme")); + + await Assert.ThrowsAsync(async () => + { + await _tenantAppService.UpdateAsync(acme.Id, new TenantUpdateDto { Name = "volosoft" }); + }); + } + + [Fact] + public async Task DeleteAsync() + { + var acme = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Name == "acme")); + + await _tenantAppService.DeleteAsync(acme.Id); + + UsingDbContext(dbContext => + { + dbContext.Tenants.Any(t => t.Id == acme.Id).ShouldBeFalse(); + }); + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj new file mode 100644 index 0000000000..e03ea985d8 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,25 @@ + + + + netcoreapp2.0 + Volo.Abp.TenantManagement.EntityFrameworkCore.Tests + Volo.Abp.TenantManagement.EntityFrameworkCore.Tests + true + false + false + false + + + + + + + + + + + + + + + diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs new file mode 100644 index 0000000000..bcaad7dd9d --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementEntityFrameworkCoreTestModule.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + [DependsOn( + typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpTenantManagementTestBaseModule) + )] + public class AbpTenantManagementEntityFrameworkCoreTestModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddEntityFrameworkInMemoryDatabase(); + + var databaseName = Guid.NewGuid().ToString(); + + services.Configure(options => + { + options.Configure(context => + { + context.DbContextOptions.UseInMemoryDatabase(databaseName); + }); + }); + + services.Configure(options => + { + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/LazyLoad_Tests.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/LazyLoad_Tests.cs new file mode 100644 index 0000000000..c1b1ac3776 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/LazyLoad_Tests.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + public class LazyLoad_Tests : LazyLoad_Tests + { + protected override void BeforeAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.PreConfigure(context => + { + context.DbContextOptions.UseLazyLoadingProxies(); + }); + }); + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantRepository_Tests.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantRepository_Tests.cs new file mode 100644 index 0000000000..7023ad78f0 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.EntityFrameworkCore.Tests/Volo/Abp/TenantManagement/EntityFrameworkCore/TenantRepository_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.TenantManagement.EntityFrameworkCore +{ + public class TenantRepository_Tests : TenantRepository_Tests + { + + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj new file mode 100644 index 0000000000..21d7382eb9 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo.Abp.TenantManagement.MongoDB.Tests.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp2.0 + Volo.Abp.TenantManagement.MongoDB.Tests + Volo.Abp.TenantManagement.MongoDB.Tests + true + false + false + false + + + + + + + + + + + + + + diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbTestModule.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbTestModule.cs new file mode 100644 index 0000000000..f1534bb254 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbTestModule.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.DependencyInjection; +using Mongo2Go; +using Volo.Abp.Data; +using Volo.Abp.Modularity; + +namespace Volo.Abp.TenantManagement.MongoDb +{ + [DependsOn( + typeof(AbpTenantManagementMongoDbModule), + typeof(AbpTenantManagementTestBaseModule) + )] + public class AbpTenantManagementMongoDbTestModule : AbpModule + { + private MongoDbRunner _mongoDbRunner; + + public override void ConfigureServices(IServiceCollection services) + { + _mongoDbRunner = MongoDbRunner.Start(); + + services.Configure(options => + { + options.ConnectionStrings.Default = _mongoDbRunner.ConnectionString; + }); + + services.AddAssemblyOf(); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + _mongoDbRunner.Dispose(); + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/TenantRepository_Tests.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/TenantRepository_Tests.cs new file mode 100644 index 0000000000..7b42e3bd32 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/TenantRepository_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.TenantManagement.MongoDb +{ + public class TenantRepository_Tests : TenantRepository_Tests + { + + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo.Abp.TenantManagement.TestBase.csproj b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo.Abp.TenantManagement.TestBase.csproj new file mode 100644 index 0000000000..546abc9661 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo.Abp.TenantManagement.TestBase.csproj @@ -0,0 +1,29 @@ + + + + netcoreapp2.0 + Volo.Abp.TenantManagement.TestBase + Volo.Abp.TenantManagement.TestBase + true + false + false + false + + + + + + + + + + + + + + + + + + + diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/AbpTenantManagementTestBaseModule.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/AbpTenantManagementTestBaseModule.cs new file mode 100644 index 0000000000..b2275b9d10 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/AbpTenantManagementTestBaseModule.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.TenantManagement +{ + [DependsOn( + typeof(AbpTenantManagementDomainModule), + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule) + )] + public class AbpTenantManagementTestBaseModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + scope.ServiceProvider + .GetRequiredService() + .Build(); + } + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/AbpTenantManagementTestDataBuilder.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/AbpTenantManagementTestDataBuilder.cs new file mode 100644 index 0000000000..45acc070d8 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/AbpTenantManagementTestDataBuilder.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.TenantManagement +{ + public class AbpTenantManagementTestDataBuilder : ITransientDependency + { + private readonly ITenantRepository _tenantRepository; + private readonly ITenantManager _tenantManager; + + public AbpTenantManagementTestDataBuilder( + ITenantRepository tenantRepository, + ITenantManager tenantManager) + { + _tenantRepository = tenantRepository; + _tenantManager = tenantManager; + } + + public void Build() + { + AsyncHelper.RunSync(AddTenantsAsync); + } + + private async Task AddTenantsAsync() + { + var acme = await _tenantManager.CreateAsync("acme"); + acme.ConnectionStrings.Add(new TenantConnectionString(acme.Id, ConnectionStrings.DefaultConnectionStringName, "DefaultConnString-Value")); + acme.ConnectionStrings.Add(new TenantConnectionString(acme.Id, "MyConnString", "MyConnString-Value")); + await _tenantRepository.InsertAsync(acme); + + var volosoft = await _tenantManager.CreateAsync("volosoft"); + await _tenantRepository.InsertAsync(volosoft); + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/LazyLoad_Tests.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/LazyLoad_Tests.cs new file mode 100644 index 0000000000..4ece524e0f --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/LazyLoad_Tests.cs @@ -0,0 +1,33 @@ +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; +using Xunit; + +namespace Volo.Abp.TenantManagement +{ + public abstract class LazyLoad_Tests : TenantManagementTestBase + where TStartupModule : IAbpModule + { + public ITenantRepository TenantRepository { get; } + + protected LazyLoad_Tests() + { + TenantRepository = GetRequiredService(); + } + + [Fact] + public async Task Should_Lazy_Load_Tenant_Collections() + { + using (var uow = GetRequiredService().Begin()) + { + var role = await TenantRepository.FindByNameAsync("acme", includeDetails: false); + role.ConnectionStrings.ShouldNotBeNull(); + role.ConnectionStrings.Any().ShouldBeTrue(); + + await uow.CompleteAsync(); + } + } + } +} \ No newline at end of file diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantManagementTestBase.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantManagementTestBase.cs new file mode 100644 index 0000000000..eee575ba5f --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantManagementTestBase.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.TenantManagement +{ + public abstract class TenantManagementTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantRepository_Tests.cs b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantRepository_Tests.cs new file mode 100644 index 0000000000..b48299ffd1 --- /dev/null +++ b/modules/tenantmanagement/test/Volo.Abp.TenantManagement.TestBase/Volo/Abp/TenantManagement/TenantRepository_Tests.cs @@ -0,0 +1,73 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; +using Xunit; + +namespace Volo.Abp.TenantManagement +{ + public abstract class TenantRepository_Tests : TenantManagementTestBase + where TStartupModule : IAbpModule + { + public ITenantRepository TenantRepository { get; } + + protected TenantRepository_Tests() + { + TenantRepository = GetRequiredService(); + } + + [Fact] + public async Task FindByNameAsync() + { + var tenant = await TenantRepository.FindByNameAsync("acme"); + tenant.ShouldNotBeNull(); + + tenant = await TenantRepository.FindByNameAsync("undefined-tenant"); + tenant.ShouldBeNull(); + + tenant = await TenantRepository.FindByNameAsync("acme", includeDetails: true); + tenant.ShouldNotBeNull(); + tenant.ConnectionStrings.Count.ShouldBeGreaterThanOrEqualTo(2); + } + + [Fact] + public async Task FindAsync() + { + var tenantId = (await TenantRepository.FindByNameAsync("acme")).Id; + + var tenant = await TenantRepository.FindAsync(tenantId); + tenant.ShouldNotBeNull(); + + tenant = await TenantRepository.FindAsync(Guid.NewGuid()); + tenant.ShouldBeNull(); + + tenant = await TenantRepository.FindAsync(tenantId, includeDetails: true); + tenant.ShouldNotBeNull(); + tenant.ConnectionStrings.Count.ShouldBeGreaterThanOrEqualTo(2); + } + + [Fact] + public async Task GetCountAsync() + { + (await TenantRepository.GetCountAsync()).ShouldBeGreaterThan(0); + } + + [Fact] + public async Task GetListAsync() + { + var tenants = await TenantRepository.GetListAsync(); + tenants.ShouldContain(t => t.Name == "acme"); + tenants.ShouldContain(t => t.Name == "volosoft"); + } + + [Fact] + public async Task Should_Eager_Load_Tenant_Collections() + { + var role = await TenantRepository.FindByNameAsync("acme"); + role.ConnectionStrings.ShouldNotBeNull(); + role.ConnectionStrings.Any().ShouldBeTrue(); + } + } +} diff --git a/modules/users/README.md b/modules/users/README.md new file mode 100644 index 0000000000..c284cf7c43 --- /dev/null +++ b/modules/users/README.md @@ -0,0 +1,2 @@ +# abp-users +Users module for ABP framework. diff --git a/modules/users/Volo.Abp.Users.sln b/modules/users/Volo.Abp.Users.sln new file mode 100644 index 0000000000..8045a66035 --- /dev/null +++ b/modules/users/Volo.Abp.Users.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27428.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Users.Domain", "src\Volo.Abp.Users.Domain\Volo.Abp.Users.Domain.csproj", "{C4147378-B0A4-4308-840F-BD75D2EFC808}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Users.EntityFrameworkCore", "src\Volo.Abp.Users.EntityFrameworkCore\Volo.Abp.Users.EntityFrameworkCore.csproj", "{F7F3F6E3-E564-49C5-8DF7-53FB390730A3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Users.Domain.Shared", "src\Volo.Abp.Users.Domain.Shared\Volo.Abp.Users.Domain.Shared.csproj", "{C18FFD6A-0BE2-4FB8-B953-895F7B9CB374}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6DE713B1-3AD7-4388-8B07-CCBE7158EC6E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{61BBB58B-4B6E-417D-A768-87B2D00BC7C3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Users.Abstractions", "src\Volo.Abp.Users.Abstractions\Volo.Abp.Users.Abstractions.csproj", "{3905D0E9-448D-43DE-8884-C60D0612AA04}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Users.MongoDB", "src\Volo.Abp.Users.MongoDB\Volo.Abp.Users.MongoDB.csproj", "{70E89492-7D4E-418A-835F-35E70282E808}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C4147378-B0A4-4308-840F-BD75D2EFC808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4147378-B0A4-4308-840F-BD75D2EFC808}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4147378-B0A4-4308-840F-BD75D2EFC808}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4147378-B0A4-4308-840F-BD75D2EFC808}.Release|Any CPU.Build.0 = Release|Any CPU + {F7F3F6E3-E564-49C5-8DF7-53FB390730A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7F3F6E3-E564-49C5-8DF7-53FB390730A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7F3F6E3-E564-49C5-8DF7-53FB390730A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7F3F6E3-E564-49C5-8DF7-53FB390730A3}.Release|Any CPU.Build.0 = Release|Any CPU + {C18FFD6A-0BE2-4FB8-B953-895F7B9CB374}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C18FFD6A-0BE2-4FB8-B953-895F7B9CB374}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C18FFD6A-0BE2-4FB8-B953-895F7B9CB374}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C18FFD6A-0BE2-4FB8-B953-895F7B9CB374}.Release|Any CPU.Build.0 = Release|Any CPU + {3905D0E9-448D-43DE-8884-C60D0612AA04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3905D0E9-448D-43DE-8884-C60D0612AA04}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3905D0E9-448D-43DE-8884-C60D0612AA04}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3905D0E9-448D-43DE-8884-C60D0612AA04}.Release|Any CPU.Build.0 = Release|Any CPU + {70E89492-7D4E-418A-835F-35E70282E808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70E89492-7D4E-418A-835F-35E70282E808}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70E89492-7D4E-418A-835F-35E70282E808}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70E89492-7D4E-418A-835F-35E70282E808}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {C4147378-B0A4-4308-840F-BD75D2EFC808} = {6DE713B1-3AD7-4388-8B07-CCBE7158EC6E} + {F7F3F6E3-E564-49C5-8DF7-53FB390730A3} = {6DE713B1-3AD7-4388-8B07-CCBE7158EC6E} + {C18FFD6A-0BE2-4FB8-B953-895F7B9CB374} = {6DE713B1-3AD7-4388-8B07-CCBE7158EC6E} + {3905D0E9-448D-43DE-8884-C60D0612AA04} = {6DE713B1-3AD7-4388-8B07-CCBE7158EC6E} + {70E89492-7D4E-418A-835F-35E70282E808} = {6DE713B1-3AD7-4388-8B07-CCBE7158EC6E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E18777C8-72AB-4C08-BA72-885650BE70A9} + EndGlobalSection +EndGlobal diff --git a/modules/users/common.props b/modules/users/common.props new file mode 100644 index 0000000000..f00a3fc2cb --- /dev/null +++ b/modules/users/common.props @@ -0,0 +1,16 @@ + + + latest + 0.3.0 + $(NoWarn);CS1591 + http://www.aspnetboilerplate.com/images/abp_nupkg.png + http://abp.io + git + https://github.com/volosoft/abp/ + + + + + + + \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo.Abp.Users.Abstractions.csproj b/modules/users/src/Volo.Abp.Users.Abstractions/Volo.Abp.Users.Abstractions.csproj new file mode 100644 index 0000000000..7224c555da --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo.Abp.Users.Abstractions.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.Users.Abstractions + Volo.Abp.Users.Abstractions + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo.Abp.Users.Abstractions.csproj.DotSettings b/modules/users/src/Volo.Abp.Users.Abstractions/Volo.Abp.Users.Abstractions.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo.Abp.Users.Abstractions.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Settings/UserSettingManagerExtensions.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Settings/UserSettingManagerExtensions.cs new file mode 100644 index 0000000000..4e3a1dd574 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Settings/UserSettingManagerExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Users; + +namespace Volo.Abp.Settings +{ + //TODO: Consider to move to another package? + + public static class UserSettingManagerExtensions + { + public static Task GetOrNullForUserAsync(this ISettingManager settingManager, [NotNull] string name, Guid userId, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, UserSettingValueProvider.ProviderName, userId.ToString(), fallback); + } + + public static Task GetOrNullForCurrentUserAsync(this ISettingManager settingManager, [NotNull] string name, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, UserSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllForUserAsync(this ISettingManager settingManager, Guid userId, bool fallback = true) + { + return settingManager.GetAllAsync(UserSettingValueProvider.ProviderName, userId.ToString(), fallback); + } + + public static Task> GetAllForCurrentUserAsync(this ISettingManager settingManager, bool fallback = true) + { + return settingManager.GetAllAsync(UserSettingValueProvider.ProviderName, null, fallback); + } + + public static Task SetForUserAsync(this ISettingManager settingManager, Guid userId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, UserSettingValueProvider.ProviderName, userId.ToString(), forceToSet); + } + + public static Task SetForCurrentUserAsync(this ISettingManager settingManager, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, UserSettingValueProvider.ProviderName, null, forceToSet); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/AbpUsersAbstractionModule.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/AbpUsersAbstractionModule.cs new file mode 100644 index 0000000000..6b66444d32 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/AbpUsersAbstractionModule.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.Settings; + +namespace Volo.Abp.Users +{ + //TODO: Consider to (somehow) move this to the framework to the same assemblily of ICurrentUser! + + public class AbpUsersAbstractionModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.ValueProviders.Add(); + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs new file mode 100644 index 0000000000..8f498e32c3 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.Users +{ + public interface IExternalUserLookupServiceProvider //TODO: Consider to inherit from IUserLookupService + { + Task FindByIdAsync(Guid id, CancellationToken cancellationToken = default); + + Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IUserData.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IUserData.cs new file mode 100644 index 0000000000..e7c0d8dab7 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IUserData.cs @@ -0,0 +1,24 @@ +using System; +using JetBrains.Annotations; + +namespace Volo.Abp.Users +{ + public interface IUserData + { + Guid Id { get; } + + Guid? TenantId { get; } + + string UserName { get; } + + [CanBeNull] + string Email { get; } + + bool EmailConfirmed { get; } + + [CanBeNull] + string PhoneNumber { get; } + + bool PhoneNumberConfirmed { get; } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/UserData.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/UserData.cs new file mode 100644 index 0000000000..7ed587cb9c --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/UserData.cs @@ -0,0 +1,45 @@ +using System; +using JetBrains.Annotations; + +namespace Volo.Abp.Users +{ + public class UserData : IUserData + { + public Guid Id { get; set; } + + public string UserName { get; set; } + + public string Email { get; set; } + + public bool EmailConfirmed { get; set; } + + public string PhoneNumber { get; set; } + + public bool PhoneNumberConfirmed { get; set; } + + public Guid? TenantId { get; set; } + + public UserData() + { + + } + + public UserData( + Guid id, + [NotNull] string userName, + [CanBeNull] string email = null, + bool emailConfirmed = false, + [CanBeNull] string phoneNumber = null, + bool phoneNumberConfirmed = false, + Guid? tenantId = null) + { + Id = id; + UserName = userName; + Email = email; + EmailConfirmed = emailConfirmed; + PhoneNumber = phoneNumber; + PhoneNumberConfirmed = phoneNumberConfirmed; + TenantId = tenantId; + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/UserSettingValueProvider.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/UserSettingValueProvider.cs new file mode 100644 index 0000000000..8beba5d31e --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/UserSettingValueProvider.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Volo.Abp.Settings; + +namespace Volo.Abp.Users +{ + //TODO: Optimization: Get all settings and cache it! + //TODO: Think if it's true to have this provider in this project? + + public class UserSettingValueProvider : SettingValueProvider + { + public const string ProviderName = "User"; + + public override string Name => ProviderName; + + protected ICurrentUser CurrentUser { get; } + + public UserSettingValueProvider(ISettingStore settingStore, ICurrentUser currentUser) + : base(settingStore) + { + CurrentUser = currentUser; + } + + public override async Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + if (providerKey == null) + { + if (CurrentUser.Id == null) + { + return null; + } + + providerKey = CurrentUser.Id.ToString(); + } + + return await SettingStore.GetOrNullAsync(setting.Name, Name, providerKey); + } + + public override Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + return SettingStore.SetAsync(setting.Name, value, Name, providerKey); + } + + public override Task ClearAsync(SettingDefinition setting, string providerKey) + { + return SettingStore.DeleteAsync(setting.Name, Name, providerKey); + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo.Abp.Users.Domain.Shared.csproj b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo.Abp.Users.Domain.Shared.csproj new file mode 100644 index 0000000000..ec7f62d4de --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo.Abp.Users.Domain.Shared.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.Users.Domain.Shared + Volo.Abp.Users.Domain.Shared + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs new file mode 100644 index 0000000000..96fecdd60e --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUserConsts.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.Users +{ + public class AbpUserConsts + { + public const int MaxUserNameLength = 256; + + public const int MaxEmailLength = 256; + + public const int MaxPhoneNumberLength = 16; + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUsersDomainSharedModule.cs b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUsersDomainSharedModule.cs new file mode 100644 index 0000000000..7a8984bbb3 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain.Shared/Volo/Abp/Users/AbpUsersDomainSharedModule.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Users +{ + public class AbpUsersDomainSharedModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo.Abp.Users.Domain.csproj b/modules/users/src/Volo.Abp.Users.Domain/Volo.Abp.Users.Domain.csproj new file mode 100644 index 0000000000..e205f2929c --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo.Abp.Users.Domain.csproj @@ -0,0 +1,22 @@ + + + + + + netstandard2.0 + Volo.Abp.Users.Domain + Volo.Abp.Users.Domain + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo.Abp.Users.Domain.csproj.DotSettings b/modules/users/src/Volo.Abp.Users.Domain/Volo.Abp.Users.Domain.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo.Abp.Users.Domain.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUserExtensions.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUserExtensions.cs new file mode 100644 index 0000000000..19dea86bf5 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUserExtensions.cs @@ -0,0 +1,18 @@ +namespace Volo.Abp.Users +{ + public static class AbpUserExtensions + { + public static IUserData ToAbpUserData(this IUser user) + { + return new UserData( + user.Id, + user.UserName, + user.Email, + user.EmailConfirmed, + user.PhoneNumber, + user.PhoneNumberConfirmed, + user.TenantId + ); + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUsersDomainModule.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUsersDomainModule.cs new file mode 100644 index 0000000000..d0fe88111b --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUsersDomainModule.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.Security; +using Volo.Abp.Settings; + +namespace Volo.Abp.Users +{ + [DependsOn( + typeof(AbpUsersDomainSharedModule), + typeof(AbpUsersAbstractionModule), + typeof(AbpSecurityModule), + typeof(AbpSettingsModule) + )] + public class AbpUsersDomainModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUpdateUserData.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUpdateUserData.cs new file mode 100644 index 0000000000..33fc3aed11 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUpdateUserData.cs @@ -0,0 +1,9 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.Users +{ + public interface IUpdateUserData + { + bool Update([NotNull] IUserData user); + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUser.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUser.cs new file mode 100644 index 0000000000..374c41a0b7 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUser.cs @@ -0,0 +1,22 @@ +using System; +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.Users +{ + public interface IUser : IAggregateRoot, IMultiTenant + { + string UserName { get; } + + [CanBeNull] + string Email { get; } + + bool EmailConfirmed { get; } + + [CanBeNull] + string PhoneNumber { get; } + + bool PhoneNumberConfirmed { get; } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs new file mode 100644 index 0000000000..2c50d7795f --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.Users +{ + public interface IUserLookupService + where TUser : class, IUser + { + Task FindByIdAsync(Guid id, CancellationToken cancellationToken = default); + + Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default); + + //TODO: More... + } +} diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs new file mode 100644 index 0000000000..14eea6a3fe --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace Volo.Abp.Users +{ + public interface IUserRepository : IBasicRepository + where TUser : class, IUser, IAggregateRoot + { + Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default); + + Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs new file mode 100644 index 0000000000..24b44be473 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs @@ -0,0 +1,129 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Uow; + +namespace Volo.Abp.Users +{ + public abstract class UserLookupService : IUserLookupService, ITransientDependency + where TUser : class, IUser + where TUserRepository : IUserRepository + { + public IExternalUserLookupServiceProvider ExternalUserLookupServiceProvider { get; set; } + public ILogger> Logger { get; set; } + + private readonly TUserRepository _userRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + protected UserLookupService( + TUserRepository userRepository, + IUnitOfWorkManager unitOfWorkManager) + { + _userRepository = userRepository; + _unitOfWorkManager = unitOfWorkManager; + + Logger = NullLogger>.Instance; + } + + public async Task FindByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + if (ExternalUserLookupServiceProvider == null) + { + return await _userRepository.FindAsync(id, cancellationToken: cancellationToken); + } + + IUserData externalUser; + + try + { + externalUser = await ExternalUserLookupServiceProvider.FindByIdAsync(id, cancellationToken); + if (externalUser == null) + { + //TODO: Instead of deleting, should be make it inactive or something like that? + await WithNewUowAsync(() => _userRepository.DeleteAsync(id, cancellationToken: cancellationToken)); + + return null; + } + } + catch (Exception ex) + { + Logger.LogException(ex); + return await _userRepository.FindAsync(id, cancellationToken: cancellationToken); + } + + var localUser = await _userRepository.FindAsync(id, cancellationToken: cancellationToken); + if (localUser == null) + { + await WithNewUowAsync(() => _userRepository.InsertAsync(CreateUser(externalUser), cancellationToken: cancellationToken)); + + return await _userRepository.FindAsync(id, cancellationToken: cancellationToken); + } + + if (localUser is IUpdateUserData && ((IUpdateUserData)localUser).Update(externalUser)) + { + await WithNewUowAsync(() => _userRepository.UpdateAsync(localUser, cancellationToken: cancellationToken)); + } + + return await _userRepository.FindAsync(id, cancellationToken: cancellationToken); + } + + public async Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default) + { + if (ExternalUserLookupServiceProvider == null) + { + return await _userRepository.FindByUserNameAsync(userName, cancellationToken); + } + + IUserData externalUser; + + try + { + externalUser = await ExternalUserLookupServiceProvider.FindByUserNameAsync(userName, cancellationToken); + if (externalUser == null) + { + var localExistingUser = await _userRepository.FindByUserNameAsync(userName, cancellationToken); + if (localExistingUser != null) + { + //TODO: Instead of deleting, should be make it passive or something like that? + await WithNewUowAsync(() => _userRepository.DeleteAsync(localExistingUser.Id, cancellationToken: cancellationToken)); + } + + return null; + } + } + catch (Exception ex) + { + Logger.LogException(ex); + return await _userRepository.FindByUserNameAsync(userName, cancellationToken); + } + + var localUser = await _userRepository.FindByUserNameAsync(userName, cancellationToken); + if (localUser == null) + { + await WithNewUowAsync(() => _userRepository.InsertAsync(CreateUser(externalUser), cancellationToken: cancellationToken)); + return await _userRepository.FindAsync(externalUser.Id, cancellationToken: cancellationToken); + } + + if (localUser is IUpdateUserData && ((IUpdateUserData)localUser).Update(externalUser)) + { + await WithNewUowAsync(() => _userRepository.UpdateAsync(localUser, cancellationToken: cancellationToken)); + } + + return await _userRepository.FindAsync(externalUser.Id, cancellationToken: cancellationToken); + } + + protected abstract TUser CreateUser(IUserData externalUser); + + private async Task WithNewUowAsync(Func func) + { + using (var uow = _unitOfWorkManager.Begin(requiresNew: true)) + { + await func(); + await uow.SaveChangesAsync(); + } + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupServiceExtensions.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupServiceExtensions.cs new file mode 100644 index 0000000000..88781ea026 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupServiceExtensions.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Users +{ + public static class UserLookupServiceExtensions + { + public static async Task GetByIdAsync(this IUserLookupService userLookupService, Guid id, CancellationToken cancellationToken = default) + where TUser : class, IUser + { + var user = await userLookupService.FindByIdAsync(id, cancellationToken); + if (user == null) + { + throw new EntityNotFoundException(typeof(TUser), id); + } + + return user; + } + + public static async Task GetByUserNameAsync(this IUserLookupService userLookupService, string userName, CancellationToken cancellationToken = default) + where TUser : class, IUser + { + var user = await userLookupService.FindByUserNameAsync(userName, cancellationToken); + if (user == null) + { + throw new EntityNotFoundException(typeof(TUser), userName); + } + + return user; + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo.Abp.Users.EntityFrameworkCore.csproj b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo.Abp.Users.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..8fc36ea81c --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo.Abp.Users.EntityFrameworkCore.csproj @@ -0,0 +1,21 @@ + + + + + + netstandard2.0 + Volo.Abp.Users.EntityFrameworkCore + Volo.Abp.Users.EntityFrameworkCore + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + diff --git a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo.Abp.Users.EntityFrameworkCore.csproj.DotSettings b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo.Abp.Users.EntityFrameworkCore.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo.Abp.Users.EntityFrameworkCore.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersDbContextModelCreatingExtensions.cs b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersDbContextModelCreatingExtensions.cs new file mode 100644 index 0000000000..1367fc423e --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersDbContextModelCreatingExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Volo.Abp.EntityFrameworkCore.Modeling; + +namespace Volo.Abp.Users.EntityFrameworkCore +{ + public static class AbpUsersDbContextModelCreatingExtensions + { + public static void ConfigureAbpUser(this EntityTypeBuilder b, ModelBuilderConfigurationOptions options) + where TUser : class, IUser + { + b.Property(u => u.TenantId).HasColumnName(nameof(IUser.TenantId)); + b.Property(u => u.UserName).IsRequired().HasMaxLength(AbpUserConsts.MaxUserNameLength).HasColumnName(nameof(IUser.UserName)); + b.Property(u => u.Email).HasMaxLength(AbpUserConsts.MaxEmailLength).HasColumnName(nameof(IUser.Email)); + b.Property(u => u.EmailConfirmed).HasDefaultValue(false).HasColumnName(nameof(IUser.EmailConfirmed)); + b.Property(u => u.PhoneNumber).HasMaxLength(AbpUserConsts.MaxPhoneNumberLength).HasColumnName(nameof(IUser.PhoneNumber)); + b.Property(u => u.PhoneNumberConfirmed).HasDefaultValue(false).HasColumnName(nameof(IUser.PhoneNumberConfirmed)); + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersEntityFrameworkCoreModule.cs b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersEntityFrameworkCoreModule.cs new file mode 100644 index 0000000000..375a42ea66 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/AbpUsersEntityFrameworkCoreModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Users.EntityFrameworkCore +{ + [DependsOn( + typeof(AbpUsersDomainModule), + typeof(AbpEntityFrameworkCoreModule) + )] + public class AbpUsersEntityFrameworkCoreModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs new file mode 100644 index 0000000000..5d0958101c --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.Users.EntityFrameworkCore +{ + public abstract class EfCoreUserRepositoryBase : EfCoreRepository, IUserRepository + where TDbContext : IEfCoreDbContext + where TUser : class, IUser + { + protected EfCoreUserRepositoryBase(IDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public async Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default) + { + return await this.FirstOrDefaultAsync(u => u.UserName == userName, GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default) + { + return await DbSet.Where(u => ids.Contains(u.Id)).ToListAsync(GetCancellationToken(cancellationToken)); + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.MongoDB/Volo.Abp.Users.MongoDB.csproj b/modules/users/src/Volo.Abp.Users.MongoDB/Volo.Abp.Users.MongoDB.csproj new file mode 100644 index 0000000000..dae8ecf8db --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.MongoDB/Volo.Abp.Users.MongoDB.csproj @@ -0,0 +1,21 @@ + + + + + + netstandard2.0 + Volo.Abp.Users.MongoDB + Volo.Abp.Users.MongoDB + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + diff --git a/modules/users/src/Volo.Abp.Users.MongoDB/Volo.Abp.Users.MongoDB.csproj.DotSettings b/modules/users/src/Volo.Abp.Users.MongoDB/Volo.Abp.Users.MongoDB.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.MongoDB/Volo.Abp.Users.MongoDB.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/AbpUsersMongoDbModule.cs b/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/AbpUsersMongoDbModule.cs new file mode 100644 index 0000000000..ef10cc9e76 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/AbpUsersMongoDbModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.Users.MongoDB +{ + [DependsOn( + typeof(AbpUsersDomainModule), + typeof(AbpMongoDbModule) + )] + public class AbpUsersMongoDbModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs b/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs new file mode 100644 index 0000000000..3c5bb66b25 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.Users.MongoDB +{ + public abstract class MongoUserRepositoryBase : MongoDbRepository, IUserRepository + where TDbContext : IAbpMongoDbContext + where TUser : class, IUser + { + protected MongoUserRepositoryBase(IMongoDbContextProvider dbContextProvider) + : base(dbContextProvider) + { + + } + + public virtual async Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default) + { + return await GetMongoQueryable().FirstOrDefaultAsync(u => u.UserName == userName, GetCancellationToken(cancellationToken)); + } + + public virtual async Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default) + { + return await GetMongoQueryable().Where(u => ids.Contains(u.Id)) .ToListAsync(GetCancellationToken(cancellationToken)); + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo.Abp.Users.Tests.Shared.csproj b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo.Abp.Users.Tests.Shared.csproj new file mode 100644 index 0000000000..755a46fb65 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo.Abp.Users.Tests.Shared.csproj @@ -0,0 +1,31 @@ + + + + netcoreapp2.0 + Volo.Abp.Users.Tests.Shared + Volo.Abp.Users.Tests.Shared + latest + true + false + false + false + + + + + + + + + + + + + + + + + + + + diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo.Abp.Users.Tests.Shared.csproj.DotSettings b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo.Abp.Users.Tests.Shared.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo.Abp.Users.Tests.Shared.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUserRepository_Tests.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUserRepository_Tests.cs new file mode 100644 index 0000000000..40d8de9572 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUserRepository_Tests.cs @@ -0,0 +1,53 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.Users +{ + public abstract class AbpUserRepository_Tests : AbpUsersTestBase + where TStartupModule : IAbpModule + { + private readonly AbpUsersLocalTestData _localTestData; + private readonly IAbpUserRepository _userRepository; + + protected AbpUserRepository_Tests() + { + _userRepository = GetRequiredService(); + _localTestData = GetRequiredService(); + } + + [Fact] + public async Task FindAsync() + { + var john = await _userRepository.FindAsync(_localTestData.John.Id); + john.ShouldNotBeNull(); + john.UserName.ShouldBe(_localTestData.John.UserName); + + //Undefined user + (await _userRepository.FindAsync(Guid.NewGuid())).ShouldBeNull(); + } + + [Fact] + public async Task FindByUserNameAsync() + { + var john = await _userRepository.FindByUserNameAsync(_localTestData.John.UserName); + john.ShouldNotBeNull(); + john.Id.ShouldBe(_localTestData.John.Id); + + //Undefined user + (await _userRepository.FindByUserNameAsync("undefined-user")).ShouldBeNull(); + } + + [Fact] + public async Task GetListAsync() + { + (await _userRepository.GetListAsync(new Guid[0])).Any().ShouldBeFalse(); + (await _userRepository.GetListAsync(new[] { _localTestData.John.Id })).Count.ShouldBe(1); + (await _userRepository.GetListAsync(new[] { _localTestData.John.Id, _localTestData.David.Id })).Count.ShouldBe(2); + (await _userRepository.GetListAsync(new[] { _localTestData.John.Id, _localTestData.David.Id, Guid.NewGuid() })).Count.ShouldBe(2); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersExternalTestData.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersExternalTestData.cs new file mode 100644 index 0000000000..dba3c573de --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersExternalTestData.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Users +{ + public class AbpUsersExternalTestData : ISingletonDependency + { + public IAbpUserData David { get; } + public IAbpUserData Neo { get; } + + public AbpUsersExternalTestData(AbpUsersLocalTestData localTestData) + { + Neo = new AbpUserData(Guid.NewGuid(), "neo"); + David = localTestData.David.ToAbpUserData(); + } + + public List GetAllUsers() + { + return new List + { + David, + Neo + }; + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersLocalTestData.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersLocalTestData.cs new file mode 100644 index 0000000000..2382a3f93e --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersLocalTestData.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Users +{ + public class AbpUsersLocalTestData : ISingletonDependency + { + public AbpUser John { get; } + public AbpUser David { get; } + + public AbpUsersLocalTestData() + { + John = new AbpUser(Guid.NewGuid(), "john"); + David = new AbpUser(Guid.NewGuid(), "david", "david@abp.io"); + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestBase.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestBase.cs new file mode 100644 index 0000000000..2d90e59d4a --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestBase.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.Users +{ + public abstract class AbpUsersTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestDataBuilder.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestDataBuilder.cs new file mode 100644 index 0000000000..a3602491be --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestDataBuilder.cs @@ -0,0 +1,29 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Users +{ + public class AbpUsersTestDataBuilder : ITransientDependency + { + private readonly IAbpUserRepository _userRepository; + private readonly AbpUsersLocalTestData _localTestData; + + public AbpUsersTestDataBuilder( + IAbpUserRepository userRepository, + AbpUsersLocalTestData localTestData) + { + _userRepository = userRepository; + _localTestData = localTestData; + } + + public void Build() + { + AddUsers(); + } + + private void AddUsers() + { + _userRepository.Insert(_localTestData.John); + _userRepository.Insert(_localTestData.David); + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestsSharedModule.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestsSharedModule.cs new file mode 100644 index 0000000000..50da79183d --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/AbpUsersTestsSharedModule.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Users +{ + [DependsOn( + typeof(AbpUsersDomainModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule) + )] + public class AbpUsersTestsSharedModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddAssemblyOf(); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + scope.ServiceProvider + .GetRequiredService() + .Build(); + } + } + } +} diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/ExternalUserLookupService_Tests.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/ExternalUserLookupService_Tests.cs new file mode 100644 index 0000000000..8916607ea7 --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/ExternalUserLookupService_Tests.cs @@ -0,0 +1,77 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.Users +{ + public abstract class ExternalUserLookupService_Tests : AbpUsersTestBase + where TStartupModule : IAbpModule + { + private readonly IAbpUserLookupService _lookupService; + private readonly AbpUsersLocalTestData _localTestData; + private readonly AbpUsersExternalTestData _externalTestData; + + protected ExternalUserLookupService_Tests() + { + _lookupService = GetRequiredService(); + _localTestData = GetRequiredService(); + _externalTestData = GetRequiredService(); + } + + protected override void AfterAddApplication(IServiceCollection services) + { + services.AddTransient(); + } + + [Fact] + public async Task FindByUserNameAsync() + { + (await GetRequiredService().FindByUserNameAsync(_localTestData.John.UserName)).ShouldNotBeNull(); + + (await _lookupService.FindByUserNameAsync("undefined-user")).ShouldBeNull(); + (await _lookupService.FindByUserNameAsync(_localTestData.John.UserName)).ShouldBeNull(); //Because it's not available in the external provider. And this will delete the user! + (await _lookupService.FindByUserNameAsync(_localTestData.David.UserName)).ShouldNotBeNull(); + (await _lookupService.FindByUserNameAsync(_externalTestData.Neo.UserName)).ShouldNotBeNull(); + + (await GetRequiredService().FindByUserNameAsync(_localTestData.John.UserName)).ShouldBeNull(); + } + + [Fact] + public async Task FindByIdAsync() + { + (await GetRequiredService().FindAsync(_localTestData.John.Id)).ShouldNotBeNull(); + + (await _lookupService.FindByIdAsync(Guid.NewGuid())).ShouldBeNull(); + (await _lookupService.FindByIdAsync(_localTestData.John.Id)).ShouldBeNull(); //Because it's not available in the external provider. And this will delete the user! + (await _lookupService.FindByIdAsync(_localTestData.David.Id)).ShouldNotBeNull(); + (await _lookupService.FindByIdAsync(_externalTestData.Neo.Id)).ShouldNotBeNull(); + + (await GetRequiredService().FindAsync(_localTestData.John.Id)).ShouldBeNull(); + } + + public class TestExternalAbpUserLookupServiceProvider : IExternalAbpUserLookupServiceProvider + { + private readonly AbpUsersExternalTestData _externalTestData; + + public TestExternalAbpUserLookupServiceProvider(AbpUsersExternalTestData externalTestData) + { + _externalTestData = externalTestData; + } + + public Task FindByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + return Task.FromResult(_externalTestData.GetAllUsers().FirstOrDefault(u => u.Id == id)); + } + + public Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default) + { + return Task.FromResult(_externalTestData.GetAllUsers().FirstOrDefault(u => u.UserName == userName)); + } + } + } +} \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/LocalUserLookupService_Tests.cs b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/LocalUserLookupService_Tests.cs new file mode 100644 index 0000000000..9e128f14cc --- /dev/null +++ b/modules/users/src/Volo.Abp.Users.Tests.Shared/Volo/Abp/Users/LocalUserLookupService_Tests.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Modularity; +using Xunit; + +namespace Volo.Abp.Users +{ + public abstract class LocalUserLookupService_Tests : AbpUsersTestBase + where TStartupModule : IAbpModule + { + private readonly IAbpUserLookupService _lookupService; + private readonly AbpUsersLocalTestData _localTestData; + + protected LocalUserLookupService_Tests() + { + _lookupService = GetRequiredService(); + _localTestData = GetRequiredService(); + } + + [Fact] + public async Task FindByUserNameAsync() + { + (await _lookupService.FindByUserNameAsync(_localTestData.John.UserName)).ShouldNotBeNull(); + (await _lookupService.FindByUserNameAsync(_localTestData.David.UserName)).ShouldNotBeNull(); + (await _lookupService.FindByUserNameAsync("undefined-user")).ShouldBeNull(); + } + } +} diff --git a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj new file mode 100644 index 0000000000..8960a35c99 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,25 @@ + + + + netcoreapp2.0 + Volo.Abp.Users.EntityFrameworkCore.Tests + Volo.Abp.Users.EntityFrameworkCore.Tests + latest + true + false + false + false + + + + + + + + + + + + + + diff --git a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj.DotSettings b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj.DotSettings new file mode 100644 index 0000000000..58ad6c8854 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo.Abp.Users.EntityFrameworkCore.Tests.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/AbpUserRepository_Tests.cs b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/AbpUserRepository_Tests.cs new file mode 100644 index 0000000000..7f92ae7483 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/AbpUserRepository_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Users.EntityFrameworkCore +{ + public class AbpUserRepository_Tests : AbpUserRepository_Tests + { + + } +} diff --git a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/AbpUsersEntityFrameworkCoreTestModule.cs b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/AbpUsersEntityFrameworkCoreTestModule.cs new file mode 100644 index 0000000000..cba8126af0 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/AbpUsersEntityFrameworkCoreTestModule.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; + +namespace Volo.Abp.Users.EntityFrameworkCore +{ + [DependsOn( + typeof(AbpUsersTestsSharedModule), + typeof(AbpUsersEntityFrameworkCoreModule) + )] + public class AbpUsersEntityFrameworkCoreTestModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddEntityFrameworkInMemoryDatabase(); + + var databaseName = Guid.NewGuid().ToString(); + + services.Configure(options => + { + options.Configure(context => + { + context.DbContextOptions.UseInMemoryDatabase(databaseName); + }); + }); + + services.Configure(options => + { + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/ExternalUserLookupService_Tests.cs b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/ExternalUserLookupService_Tests.cs new file mode 100644 index 0000000000..26fd633808 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/ExternalUserLookupService_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Users.EntityFrameworkCore +{ + public class ExternalUserLookupService_Tests : ExternalUserLookupService_Tests + { + + } +} \ No newline at end of file diff --git a/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/LocalUserLookupService_Tests.cs b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/LocalUserLookupService_Tests.cs new file mode 100644 index 0000000000..089fd35dbe --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.EntityFrameworkCore.Tests/Volo/Abp/Users/EntityFrameworkCore/LocalUserLookupService_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Users.EntityFrameworkCore +{ + public class LocalUserLookupService_Tests : LocalUserLookupService_Tests + { + + } +} \ No newline at end of file diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo.Abp.Users.MongoDB.Tests.csproj b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo.Abp.Users.MongoDB.Tests.csproj new file mode 100644 index 0000000000..a75e23f948 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo.Abp.Users.MongoDB.Tests.csproj @@ -0,0 +1,25 @@ + + + + netcoreapp2.0 + Volo.Abp.Users.MongoDB.Tests + Volo.Abp.Users.MongoDB.Tests + latest + true + false + false + false + + + + + + + + + + + + + + diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUserRepository_Tests.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUserRepository_Tests.cs new file mode 100644 index 0000000000..7f0f5620db --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUserRepository_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Users.MongoDB +{ + public class AbpUserRepository_Tests : AbpUserRepository_Tests + { + + } +} diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUsersMongoDbTestModule.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUsersMongoDbTestModule.cs new file mode 100644 index 0000000000..65963cc6e6 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/AbpUsersMongoDbTestModule.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.DependencyInjection; +using Mongo2Go; +using Volo.Abp.Data; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Users.MongoDB +{ + [DependsOn( + typeof(AbpUsersMongoDbModule), + typeof(AbpUsersTestsSharedModule) + )] + public class AbpUsersMongoDbTestModule : AbpModule + { + private MongoDbRunner _mongoDbRunner; + + public override void ConfigureServices(IServiceCollection services) + { + _mongoDbRunner = MongoDbRunner.Start(); + + services.Configure(options => + { + options.ConnectionStrings.Default = _mongoDbRunner.ConnectionString; + }); + + services.AddAssemblyOf(); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + _mongoDbRunner.Dispose(); + } + } +} diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/ExternalUserLookupService_Tests.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/ExternalUserLookupService_Tests.cs new file mode 100644 index 0000000000..451ef8baae --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/ExternalUserLookupService_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Users.MongoDB +{ + public class ExternalUserLookupService_Tests : ExternalUserLookupService_Tests + { + + } +} \ No newline at end of file diff --git a/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/LocalUserLookupService_Tests.cs b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/LocalUserLookupService_Tests.cs new file mode 100644 index 0000000000..26f9aa1ac5 --- /dev/null +++ b/modules/users/test/Volo.Abp.Users.MongoDB.Tests/Volo/Abp/Users/MongoDB/LocalUserLookupService_Tests.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Users.MongoDB +{ + public class LocalUserLookupService_Tests : LocalUserLookupService_Tests + { + + } +} \ No newline at end of file