├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CodeCoverage.runsettings ├── Directory.Build.targets ├── IntegrationTests.playlist ├── LICENSE ├── README.md ├── UnitTests.playlist ├── UserManager.BusinessLogic.Tests ├── DataAccess │ ├── DatabaseServiceTests.cs │ └── Mappers │ │ ├── EmailMapperTests.cs │ │ └── RoleMapperTests.cs ├── Model │ ├── EmailAttributeTests.cs │ ├── EmailTests.cs │ └── RoleTests.cs ├── Properties │ └── AssemblyInfo.cs ├── UserManager.BusinessLogic.Tests.csproj └── packages.config ├── UserManager.BusinessLogic ├── DataAccess │ ├── ConfigureDapper.cs │ ├── DapperExtensions.cs │ ├── DatabaseContext.cs │ ├── DatabaseService.cs │ ├── IDatabaseService.cs │ ├── Mappers │ │ ├── EmailMapper.cs │ │ ├── IntTypeHandler.cs │ │ └── RoleMapper.cs │ └── Repositories │ │ ├── DatabaseRepository.cs │ │ ├── IDatabaseRepository.cs │ │ ├── IRoleRepository.cs │ │ ├── IUserRepository.cs │ │ ├── RoleRepository.cs │ │ └── UserRepository.cs ├── Exceptions │ └── NotFoundException.cs ├── Extensions │ └── LogExtensions.cs ├── Model │ ├── Email.cs │ ├── EmailAttribute.cs │ ├── Role.cs │ └── User.cs ├── Properties │ └── AssemblyInfo.cs ├── UserManager.BusinessLogic.csproj └── packages.config ├── UserManager.DataAccess.Migrations ├── App.config ├── Command.cs ├── FluentMigratorExtensions.cs ├── Migrations │ ├── 20201024202704_InitialMigration.cs │ ├── 20201024202825_AddUserTable.cs │ ├── 20201113194828_AddRoleToUser.cs │ └── 20210114223404_AddCreationDateToUser.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── UserManager.DataAccess.Migrations.csproj └── packages.config ├── UserManager.IntegrationTests ├── Main │ ├── MainMenuTests.cs │ └── SettingsMenuTests.cs ├── Properties │ └── AssemblyInfo.cs ├── TestUtils │ ├── AppWindow.cs │ ├── Initialize.cs │ └── TestBase.cs ├── UserManager.IntegrationTests.csproj ├── Users │ ├── BaseUserView.cs │ ├── UserCreationTests.cs │ └── UserListTests.cs └── packages.config ├── UserManager.Resources ├── General.Designer.cs ├── General.es.Designer.cs ├── General.es.resx ├── General.resx ├── Properties │ └── AssemblyInfo.cs └── UserManager.Resources.csproj ├── UserManager.Tests ├── Mappers │ ├── RoleProfileTests.cs │ └── UserProfileTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Startup │ └── ExecutionParametersTests.cs ├── TestUtils │ ├── ArgExt.cs │ ├── Fakes │ │ └── FakeSnackbarMessage.cs │ ├── IMessageHubExtensions.cs │ ├── MappingAssertions.cs │ ├── ObjectComparer.cs │ ├── PropertyChangeListener.cs │ └── TestMapperProfile.cs ├── UserManager.Tests.csproj ├── ViewModels │ ├── CreateUserViewModelTests.cs │ ├── MainViewModelTests.cs │ ├── RolesViewModelTests.cs │ └── UsersViewModelTests.cs ├── app.config └── packages.config ├── UserManager.sln ├── UserManager ├── App.config ├── DTOs │ ├── CreateUserDto.cs │ ├── DatabaseConnectionDto.cs │ ├── LanguageDto.cs │ ├── RoleListItemDto.cs │ ├── RoleSelectDto.cs │ └── UserListItemDto.cs ├── Events │ └── UserCreatedEvent.cs ├── Mappers │ ├── RoleProfile.cs │ └── UserProfile.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Providers │ ├── ISettingProvider.cs │ └── SettingProvider.cs ├── Startup │ ├── AutomapperConfig.cs │ ├── ExceptionHandling.cs │ ├── ExecutionParameters.cs │ ├── GlobalizationConfig.cs │ ├── IoCConfig.cs │ ├── LogConfig.cs │ └── TelemetryConfig.cs ├── UserManager.csproj ├── ViewModels │ ├── CreateUserViewModel.cs │ ├── MainViewModel.cs │ ├── RolesViewModel.cs │ └── UsersViewModel.cs ├── Views │ ├── CreateUserView.Designer.cs │ ├── CreateUserView.cs │ ├── CreateUserView.resx │ ├── MainView.Designer.cs │ ├── MainView.cs │ ├── MainView.resx │ ├── RolesView.Designer.cs │ ├── RolesView.cs │ ├── RolesView.resx │ ├── UsersView.Designer.cs │ ├── UsersView.cs │ └── UsersView.resx └── packages.config ├── WinformsTools.Common.Tests ├── Extensions │ ├── CollectionExtensionsTests.cs │ └── PropertyInfoExtensionsTests.cs ├── Helpers │ └── MagicToolboxTests.cs ├── Properties │ └── AssemblyInfo.cs ├── SystemContextTests.cs ├── WinformsTools.Common.Tests.csproj └── packages.config ├── WinformsTools.Common ├── Extensions │ ├── CollectionExtensions.cs │ └── PropertyInfoExtensions.cs ├── Helpers │ ├── AsyncHelper.cs │ └── MagicToolbox.cs ├── Properties │ └── AssemblyInfo.cs ├── SystemContext.cs └── WinformsTools.Common.csproj ├── WinformsTools.IntegrationTestUtils.Tests ├── Elements │ └── DialogResultMapperTests.cs ├── Properties │ └── AssemblyInfo.cs ├── WinformsTools.IntegrationTestUtils.Tests.csproj └── packages.config ├── WinformsTools.IntegrationTestUtils ├── BaseAppWindow.cs ├── ElementNotFoundException.cs ├── Elements │ ├── DialogOption.cs │ ├── DialogResultMapper.cs │ ├── ModalElement.cs │ └── VisualForm.cs ├── Extensions │ ├── AutomationElementQueryExtensions.cs │ ├── CustomLocalizedStrings.cs │ ├── DataGridViewExtensions.cs │ └── FlaUiExtensions.cs ├── Properties │ └── AssemblyInfo.cs ├── WinformsTools.IntegrationTestUtils.csproj └── packages.config ├── WinformsTools.MVVM.Tests ├── Bindings │ ├── AdvancedBindingListTests.cs │ ├── ListObjectComparerTests.cs │ └── ReflectionUtilsTests.cs ├── Controls │ ├── ControlExtensionsTests.cs │ └── DataGridViewControl │ │ ├── BindedAdvancedDataGridViewTests.cs │ │ ├── FilterClauseTests.cs │ │ └── FilterConverterTests.cs ├── Core │ └── CommandTests.cs ├── Navigation │ ├── RegisteredViewsTests.cs │ ├── ViewClosedEventTests.cs │ └── ViewNavigatorTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Telemetry │ ├── ClickMetricTests.cs │ ├── MetricsTests.cs │ └── ViewEnteredMetricTests.cs ├── TestUtils │ ├── DataGridViewExtensions.cs │ ├── Fakes │ │ ├── FakeView.cs │ │ └── FakeViewModel.cs │ └── SerilogFormatter.cs ├── WinformsTools.MVVM.Tests.csproj ├── app.config └── packages.config └── WinformsTools.MVVM ├── Bindings ├── AdvancedBindingList.cs ├── Bind.cs ├── BindableObject.cs ├── BindingExtensions.cs ├── ClickBindingExtensions.cs ├── ComboBoxExtensions.cs ├── ComboBoxSource.cs ├── ConversionBinding.cs ├── Converters │ └── InverseBoolConverter.cs ├── DataGridViewBindingExtensions.cs ├── ListObjectComparer.cs ├── PropertyComparer.cs └── ReflectionUtils.cs ├── Components ├── ApplicationDispatcher.cs ├── GenericValidator.cs ├── IMessageDialog.cs └── MessageDialog.cs ├── Controls ├── BindableToolStripMenuItem.cs ├── ControlExtensions.cs ├── DataGridViewControl │ ├── FilterClause.cs │ ├── FilterConverter.cs │ └── TableColumnAttribute.cs ├── IMenuOption.cs ├── IconExtensions.cs ├── MenuOption.cs └── SnackbarControl │ ├── SnackbarControl.Designer.cs │ ├── SnackbarControl.cs │ ├── SnackbarControl.resx │ ├── SnackbarMessage.cs │ ├── SnackbarMessageProvider.cs │ └── SnackbarProvider.cs ├── Core ├── Command.cs ├── ICommand.cs ├── IView.cs └── IViewModel.cs ├── DependencyInjection ├── GreediestConstructorBehavior.cs └── SimpleInjectorExtensions.cs ├── Navigation ├── IViewNavigator.cs ├── RegisteredViews.cs ├── ViewClosedEvent.cs └── ViewNavigator.cs ├── Properties └── AssemblyInfo.cs ├── Telemetry ├── ClickMetric.cs ├── IMetric.cs ├── Metrics.cs └── ViewEnteredMetric.cs ├── Validations └── Validation.cs ├── WinformsTools.MVVM.csproj └── packages.config /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | pull_request: 9 | branches: 10 | - master 11 | 12 | env: 13 | SOLUTION_NAME: UserManager.sln 14 | 15 | jobs: 16 | build: 17 | runs-on: windows-latest 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | 23 | - name: Setup MSBuild 24 | uses: microsoft/setup-msbuild@v1 25 | 26 | - name: Setup NuGet 27 | uses: NuGet/setup-nuget@v1.0.5 28 | 29 | - name: Restore NuGet packages 30 | run: nuget restore ${{ env.SOLUTION_NAME }} 31 | 32 | - name: Build solution 33 | run: msbuild ${{ env.SOLUTION_NAME }} /verbosity:quiet /p:Configuration=Release 34 | 35 | - name: Setup VSTest 36 | uses: darenm/Setup-VSTest@v1 37 | 38 | - name: Run Unit tests 39 | run: vstest.console.exe **/bin/**/*.Tests.dll /TestCaseFilter:"TestCategory=UnitTests" /Parallel /Logger:"trx" 40 | 41 | - name: Publish test results 42 | uses: dorny/test-reporter@v1 43 | if: success() || failure() 44 | with: 45 | name: Unit Tests 46 | path: TestResults/*.trx 47 | reporter: dotnet-trx 48 | -------------------------------------------------------------------------------- /CodeCoverage.runsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | .*WinformsTools[\w|\d|\.|-]*\.dll$ 12 | .*UserManager[\w|\d|\.|-]*\.dll$ 13 | .*UserManager.exe$ 14 | 15 | 16 | .*Tests.dll$ 17 | .*TestUtils.dll$ 18 | 19 | 20 | 21 | 22 | .*\\*.Designer.cs 23 | 24 | 25 | 26 | 27 | 28 | ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$ 29 | 30 | 31 | 32 | 33 | True 34 | True 35 | True 36 | False 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | ..\TestResults 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /IntegrationTests.playlist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 A Bravo Dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UnitTests.playlist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic.Tests/DataAccess/Mappers/EmailMapperTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Data.SqlClient; 6 | using UserManager.BusinessLogic.DataAccess.Mappers; 7 | using UserManager.BusinessLogic.Model; 8 | 9 | namespace UserManager.BusinessLogic.Tests.DataAccess.Mappers 10 | { 11 | [TestClass] 12 | public class EmailMapperTests 13 | { 14 | [TestMethod] 15 | public void Parse_Null_ReturnNull() 16 | { 17 | // Arrange 18 | var mapper = new EmailMapper(); 19 | 20 | // Act 21 | var email = mapper.Parse(null); 22 | 23 | // Assert 24 | email.Should().BeNull(); 25 | } 26 | 27 | [TestMethod] 28 | public void Parse_ValidEmail_ReturnParsedEmail() 29 | { 30 | // Arrange 31 | var emailAddress = "valid_email@mail.com"; 32 | var mapper = new EmailMapper(); 33 | 34 | // Act 35 | var email = mapper.Parse(emailAddress); 36 | 37 | // Assert 38 | email.ToString().Should().Be(emailAddress); 39 | } 40 | 41 | [TestMethod] 42 | public void Parse_InvalidEmail_ThrowException() 43 | { 44 | // Arrange 45 | var emailAddress = "invalid_email"; 46 | var mapper = new EmailMapper(); 47 | 48 | // Act 49 | Action action = () => mapper.Parse(emailAddress); 50 | 51 | // Assert 52 | action.Should().Throw(); 53 | } 54 | 55 | [TestMethod] 56 | public void SetValue_AnyEmail_SetEmailToParameter() 57 | { 58 | // Arrange 59 | var emailAddress = "valid_email@mail.com"; 60 | var email = new Email(emailAddress); 61 | var mapper = new EmailMapper(); 62 | var parameter = new SqlParameter(); 63 | 64 | // Act 65 | mapper.SetValue(parameter, email); 66 | 67 | // Assert 68 | parameter.Value.Should().Be(emailAddress); 69 | } 70 | 71 | [TestMethod] 72 | public void SetValue_Null_SetNullToParameter() 73 | { 74 | // Arrange 75 | var mapper = new EmailMapper(); 76 | var parameter = new SqlParameter(); 77 | 78 | // Act 79 | mapper.SetValue(parameter, null); 80 | 81 | // Assert 82 | parameter.Value.Should().Be(DBNull.Value); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic.Tests/DataAccess/Mappers/RoleMapperTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Data.SqlClient; 5 | using UserManager.BusinessLogic.DataAccess.Mappers; 6 | using UserManager.BusinessLogic.Model; 7 | 8 | namespace UserManager.BusinessLogic.Tests.DataAccess.Mappers 9 | { 10 | [TestClass] 11 | public class RoleMapperTests 12 | { 13 | [TestMethod] 14 | public void Parse_Null_ReturnNull() 15 | { 16 | // Arrange 17 | var mapper = new RoleMapper(); 18 | 19 | // Act 20 | var role = mapper.Parse(null); 21 | 22 | // Assert 23 | role.Should().BeNull(); 24 | } 25 | 26 | [TestMethod] 27 | public void Parse_ValidRole_ReturnParsedRole() 28 | { 29 | // Arrange 30 | var validRole = Role.Basic; 31 | var mapper = new RoleMapper(); 32 | 33 | // Act 34 | var role = mapper.Parse(validRole.Id); 35 | 36 | // Assert 37 | role.Should().Be(validRole); 38 | } 39 | 40 | [TestMethod] 41 | public void Parse_InvalidEmail_ThrowException() 42 | { 43 | // Arrange 44 | var roleId = 0; 45 | var mapper = new RoleMapper(); 46 | 47 | // Act 48 | Action action = () => mapper.Parse(roleId); 49 | 50 | // Assert 51 | action.Should().Throw(); 52 | } 53 | 54 | [TestMethod] 55 | public void SetValue_AnyRole_SetRoleIdToParameter() 56 | { 57 | // Arrange 58 | var validRole = Role.Basic; 59 | var mapper = new RoleMapper(); 60 | var parameter = new SqlParameter(); 61 | 62 | // Act 63 | mapper.SetValue(parameter, validRole); 64 | 65 | // Assert 66 | parameter.Value.Should().Be(validRole.Id); 67 | } 68 | 69 | [TestMethod] 70 | public void SetValue_Null_SetNullToParameter() 71 | { 72 | // Arrange 73 | var mapper = new RoleMapper(); 74 | var parameter = new SqlParameter(); 75 | 76 | // Act 77 | mapper.SetValue(parameter, null); 78 | 79 | // Assert 80 | parameter.Value.Should().Be(DBNull.Value); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic.Tests/Model/EmailAttributeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using UserManager.BusinessLogic.Model; 7 | 8 | namespace UserManager.BusinessLogic.Tests.Model 9 | { 10 | 11 | [TestClass] 12 | public class EmailAttributeTests 13 | { 14 | private class UserWithMandatoryEmail 15 | { 16 | [Email] 17 | [Required] 18 | public string Email { get; set; } 19 | } 20 | 21 | private class UserWithOptionalEmail 22 | { 23 | [Email] 24 | public string Email { get; set; } 25 | } 26 | 27 | private class UserWithWrongType 28 | { 29 | [Email] 30 | public int Email { get; set; } 31 | } 32 | 33 | [TestMethod] 34 | public void IsValid_UserWithWrongType_ThrowArgumentException() 35 | { 36 | // Arrange 37 | var user = new UserWithWrongType { Email = 0 }; 38 | 39 | // Act 40 | Action action = () => ValidateModel(user); 41 | 42 | // Assert 43 | action.Should().Throw(); 44 | } 45 | 46 | [DataTestMethod] 47 | [DataRow(null, false)] 48 | [DataRow("", false)] 49 | [DataRow("valid_email@mail.com", true)] 50 | public void IsValid_UserNeedsEmail_ReturnsTrueIfAValidOne(string emailAddress, bool expectedValidity) 51 | { 52 | // Arrange 53 | var user = new UserWithMandatoryEmail { Email = emailAddress }; 54 | 55 | // Act 56 | var (isValid, _) = ValidateModel(user); 57 | 58 | // Assert 59 | isValid.Should().Be(expectedValidity); 60 | } 61 | 62 | [DataTestMethod] 63 | [DataRow(null, true)] 64 | [DataRow("", false)] 65 | [DataRow("valid_email@mail.com", true)] 66 | public void IsValid_UserDoesNotNeedEmail_ReturnsTrueIfAValidOneOrNotProvided(string emailAddress, bool expectedValidity) 67 | { 68 | // Arrange 69 | var user = new UserWithOptionalEmail { Email = emailAddress }; 70 | 71 | // Act 72 | var (isValid, _) = ValidateModel(user); 73 | 74 | // Assert 75 | isValid.Should().Be(expectedValidity); 76 | } 77 | 78 | private (bool IsValid, List Results) ValidateModel(object model) 79 | { 80 | var context = new ValidationContext(model); 81 | var results = new List(); 82 | var isValid = Validator.TryValidateObject(model, context, results, true); 83 | return (isValid, results); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic.Tests/Model/EmailTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.ComponentModel.DataAnnotations; 5 | using UserManager.BusinessLogic.Model; 6 | 7 | namespace UserManager.BusinessLogic.Tests.Model 8 | { 9 | [TestClass] 10 | public class EmailTests 11 | { 12 | [TestMethod] 13 | public void Constructor_InvalidEmail_ThrowValidationException() 14 | { 15 | // Arrange 16 | var invalidEmailAddress = "invalid_email"; 17 | 18 | // Act 19 | Action action = () => new Email(invalidEmailAddress); 20 | 21 | // Assert 22 | action.Should().Throw(); 23 | } 24 | 25 | [DataTestMethod] 26 | [DataRow("valid_email@mail.com")] 27 | [DataRow("valid.email@mail.com")] 28 | [DataRow("valid_email@mail.info")] 29 | [DataRow("valid_email@subdomain.mail.info")] 30 | public void Constructor_ValidEmail_ReturnCreatedEmail(string validEmailAddress) 31 | { 32 | // Act 33 | var email = new Email(validEmailAddress); 34 | 35 | // Assert 36 | email.ToString().Should().Be(validEmailAddress); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic.Tests/Model/RoleTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using UserManager.BusinessLogic.Model; 5 | 6 | namespace UserManager.BusinessLogic.Tests.Model 7 | { 8 | [TestClass] 9 | public class RoleTests 10 | { 11 | [TestMethod] 12 | public void FromId_WrongId_ThrowException() 13 | { 14 | // Arrange 15 | var wrongRoleId = 0; 16 | 17 | // Act 18 | Action action = () => Role.FromId(wrongRoleId); 19 | 20 | // Assert 21 | action.Should().Throw(); 22 | } 23 | 24 | [DataTestMethod] 25 | [DataRow(1)] 26 | [DataRow(2)] 27 | public void FromId_ExistingId_ReturnRole(int roleId) 28 | { 29 | // Act 30 | var role = Role.FromId(roleId); 31 | 32 | // Assert 33 | role.Id.Should().Be(roleId); 34 | } 35 | 36 | [TestMethod] 37 | public void Roles_ReturnTheListOfRoles() 38 | { 39 | // Act 40 | var roles = Role.Roles; 41 | 42 | // Assert 43 | roles.Should().BeEquivalentTo(new[] { Role.Admin, Role.Basic }); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: AssemblyTitle("UserManager.BusinessLogic.Tests")] 7 | [assembly: AssemblyDescription("")] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("UserManager.BusinessLogic.Tests")] 11 | [assembly: AssemblyCopyright("Copyright © 2021")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | [assembly: ComVisible(false)] 16 | 17 | [assembly: Guid("d59ea9a6-62bf-45c1-af5a-1e73c5d432f6")] 18 | 19 | // [assembly: AssemblyVersion("1.0.*")] 20 | [assembly: AssemblyVersion("1.0.0.0")] 21 | [assembly: AssemblyFileVersion("1.0.0.0")] 22 | 23 | [assembly: TestCategory("UnitTests")] -------------------------------------------------------------------------------- /UserManager.BusinessLogic.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/ConfigureDapper.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using UserManager.BusinessLogic.DataAccess.Mappers; 3 | 4 | namespace UserManager.BusinessLogic.DataAccess 5 | { 6 | public static class ConfigureDapper 7 | { 8 | public static void Config() 9 | { 10 | ConfigureMappers(); 11 | } 12 | 13 | private static void ConfigureMappers() 14 | { 15 | SqlMapper.AddTypeHandler(new EmailMapper()); 16 | SqlMapper.AddTypeHandler(new RoleMapper()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/DapperExtensions.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using Newtonsoft.Json; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using UserManager.BusinessLogic.Exceptions; 8 | 9 | namespace UserManager.BusinessLogic.DataAccess 10 | { 11 | public static class DapperExtensions 12 | { 13 | public static async Task> ToListAsync(this Task> source) 14 | => (await source).ToList(); 15 | 16 | public static async Task FirstOrDefaultAsync(this Task> source) 17 | => (await source).FirstOrDefault(); 18 | 19 | /// 20 | /// Execute query to return first element. It throws if not found 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | public static async Task GetFirstAsync(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null) 31 | { 32 | var item = await cnn.QueryFirstOrDefaultAsync(sql, param, transaction, commandTimeout, commandType); 33 | if(item == null) 34 | { 35 | throw new NotFoundException($"{typeof(TSource).Name} not found {JsonConvert.SerializeObject(param)}"); 36 | } 37 | return item; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/DatabaseContext.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using System.Data.SqlClient; 3 | 4 | namespace UserManager.BusinessLogic.DataAccess 5 | { 6 | public class DatabaseContext 7 | { 8 | private readonly string _connectionString; 9 | 10 | public DatabaseContext(string conectionString) 11 | { 12 | _connectionString = conectionString; 13 | } 14 | 15 | public virtual SqlConnectionStringBuilder ConnectionInfo => new SqlConnectionStringBuilder(_connectionString); 16 | 17 | protected virtual IDbConnection GetConnection() => new SqlConnection(_connectionString); 18 | 19 | /// 20 | /// Returns an opened connection 21 | /// 22 | /// 23 | public IDbConnection GetOpenedConnection() 24 | { 25 | var connection = GetConnection(); 26 | connection.Open(); 27 | return connection; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/DatabaseService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using System; 3 | using UserManager.BusinessLogic.DataAccess.Repositories; 4 | 5 | namespace UserManager.BusinessLogic.DataAccess 6 | { 7 | public class DatabaseService : IDatabaseService 8 | { 9 | private readonly DatabaseContext _databaseContext; 10 | private readonly IDatabaseRepository _databaseRepository; 11 | 12 | public DatabaseService(DatabaseContext databaseContext, IDatabaseRepository databaseRepository) 13 | { 14 | _databaseContext = databaseContext; 15 | _databaseRepository = databaseRepository; 16 | } 17 | 18 | public string GetName() => _databaseContext.ConnectionInfo.InitialCatalog; 19 | 20 | public string GetServer() => _databaseContext.ConnectionInfo.DataSource; 21 | 22 | public async Task CanConnectToDatabase() 23 | { 24 | try 25 | { 26 | var databaseVersion = await _databaseRepository.GetDatabaseVersion(); 27 | return true; 28 | } 29 | catch (Exception) 30 | { 31 | return false; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/IDatabaseService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace UserManager.BusinessLogic.DataAccess 4 | { 5 | public interface IDatabaseService 6 | { 7 | string GetName(); 8 | 9 | Task CanConnectToDatabase(); 10 | string GetServer(); 11 | } 12 | } -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Mappers/EmailMapper.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using UserManager.BusinessLogic.Model; 3 | 4 | namespace UserManager.BusinessLogic.DataAccess.Mappers 5 | { 6 | public class EmailMapper : SqlMapper.StringTypeHandler 7 | { 8 | protected override string Format(Email email) => email.ToString(); 9 | 10 | protected override Email Parse(string email) => new Email(email); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Mappers/IntTypeHandler.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using System; 3 | using System.Data; 4 | 5 | namespace UserManager.BusinessLogic.DataAccess.Mappers 6 | { 7 | /// 8 | /// Inspired by 9 | /// 10 | /// 11 | public abstract class IntTypeHandler : SqlMapper.TypeHandler 12 | { 13 | public override T Parse(object value) 14 | { 15 | if (value == null || value is DBNull) return default(T); 16 | return Parse(Convert.ToInt32(value)); 17 | } 18 | 19 | public override void SetValue(IDbDataParameter parameter, T value) 20 | { 21 | parameter.Value = value == null ? (object) DBNull.Value : Format(value); 22 | } 23 | 24 | protected abstract int Format(T xml); 25 | 26 | protected abstract T Parse(int xml); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Mappers/RoleMapper.cs: -------------------------------------------------------------------------------- 1 | using UserManager.BusinessLogic.Model; 2 | 3 | namespace UserManager.BusinessLogic.DataAccess.Mappers 4 | { 5 | public class RoleMapper : IntTypeHandler 6 | { 7 | protected override int Format(Role role) => role.Id; 8 | 9 | protected override Role Parse(int roleId) => Role.FromId(roleId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Repositories/DatabaseRepository.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using System.Threading.Tasks; 3 | 4 | namespace UserManager.BusinessLogic.DataAccess.Repositories 5 | { 6 | public class DatabaseRepository : IDatabaseRepository 7 | { 8 | private readonly DatabaseContext _context; 9 | 10 | public DatabaseRepository(DatabaseContext context) 11 | { 12 | _context = context; 13 | } 14 | 15 | public async Task GetDatabaseVersion() 16 | { 17 | using (var connection = _context.GetOpenedConnection()) 18 | { 19 | return await connection.QueryFirstAsync("SELECT @@version"); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Repositories/IDatabaseRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace UserManager.BusinessLogic.DataAccess.Repositories 4 | { 5 | public interface IDatabaseRepository 6 | { 7 | Task GetDatabaseVersion(); 8 | } 9 | } -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Repositories/IRoleRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using UserManager.BusinessLogic.Model; 4 | 5 | namespace UserManager.BusinessLogic.DataAccess.Repositories 6 | { 7 | public interface IRoleRepository 8 | { 9 | Task> GetAll(); 10 | } 11 | } -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Repositories/IUserRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using UserManager.BusinessLogic.Model; 4 | 5 | namespace UserManager.BusinessLogic.DataAccess.Repositories 6 | { 7 | public interface IUserRepository 8 | { 9 | Task GetById(int id); 10 | 11 | Task> GetAll(); 12 | 13 | Task CreateUser(User user); 14 | 15 | Task UpdateUser(User user); 16 | 17 | Task Remove(int id); 18 | } 19 | } -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Repositories/RoleRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using UserManager.BusinessLogic.Model; 5 | 6 | namespace UserManager.BusinessLogic.DataAccess.Repositories 7 | { 8 | public class RoleRepository : IRoleRepository 9 | { 10 | public Task> GetAll() => Task.FromResult(Role.Roles.ToList()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/DataAccess/Repositories/UserRepository.cs: -------------------------------------------------------------------------------- 1 | using Dapper; 2 | using Dapper.Contrib.Extensions; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using UserManager.BusinessLogic.Model; 6 | 7 | namespace UserManager.BusinessLogic.DataAccess.Repositories 8 | { 9 | public class UserRepository : IUserRepository 10 | { 11 | private readonly DatabaseContext _context; 12 | 13 | public UserRepository(DatabaseContext context) 14 | { 15 | _context = context; 16 | } 17 | 18 | public async Task CreateUser(User user) 19 | { 20 | using (var connection = _context.GetOpenedConnection()) 21 | { 22 | return await connection.InsertAsync(user); 23 | } 24 | } 25 | 26 | public async Task> GetAll() 27 | { 28 | using (var connection = _context.GetOpenedConnection()) 29 | { 30 | return await connection.GetAllAsync().ToListAsync(); 31 | } 32 | } 33 | 34 | public async Task GetById(int id) 35 | { 36 | using (var connection = _context.GetOpenedConnection()) 37 | { 38 | return await connection.GetAsync(id); 39 | } 40 | } 41 | 42 | public async Task UpdateUser(User user) 43 | { 44 | // Nothing, this is updated by reference 45 | } 46 | 47 | public async Task Remove(int id) 48 | { 49 | using (var connection = _context.GetOpenedConnection()) 50 | { 51 | await connection.ExecuteAsync("DELETE FROM [User] WHERE Id = @Id", new { Id = id }); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UserManager.BusinessLogic.Exceptions 4 | { 5 | public class NotFoundException : Exception 6 | { 7 | public NotFoundException(string message) : base(message) 8 | { 9 | 10 | } 11 | 12 | public NotFoundException(string type, string parameter, object id) : this($"{type} not found with {parameter} = {id}") 13 | { 14 | 15 | } 16 | 17 | public NotFoundException(string type, object id) : this(type, "id", id) 18 | { 19 | 20 | } 21 | } 22 | 23 | public class NotFoundException : NotFoundException 24 | { 25 | public NotFoundException(string message) : base(message) 26 | { 27 | 28 | } 29 | 30 | public NotFoundException(string parameter, object id) : base($"{typeof(T).Name} not found with {parameter} = {id}") 31 | { 32 | 33 | } 34 | 35 | public NotFoundException(object id) : base(typeof(T).Name, "id", id) 36 | { 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/Extensions/LogExtensions.cs: -------------------------------------------------------------------------------- 1 | using Serilog; 2 | using System; 3 | 4 | namespace UserManager.BusinessLogic.Extensions 5 | { 6 | public static class LogExtensions 7 | { 8 | public static void Error(this ILogger logger, Exception ex) => logger.Error(ex, "{ExceptionMessage}", ex.Message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/Model/Email.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace UserManager.BusinessLogic.Model 5 | { 6 | public class Email 7 | { 8 | private readonly string _email; 9 | 10 | public Email(string email) 11 | { 12 | new EmailValidator().Validate(email); 13 | _email = email; 14 | } 15 | 16 | public override string ToString() => _email; 17 | } 18 | 19 | public class EmailValidator 20 | { 21 | public void Validate(string email) 22 | { 23 | if (!IsValid(email)) 24 | { 25 | throw new ValidationException($"Invalid Email: {email}"); 26 | } 27 | } 28 | 29 | /// 30 | /// Validate email based on 31 | /// 32 | /// 33 | /// 34 | public bool IsValid(string email) 35 | { 36 | return new EmailAddressAttribute().IsValid(email); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /UserManager.BusinessLogic/Model/EmailAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace UserManager.BusinessLogic.Model 6 | { 7 | 8 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] 9 | public class EmailAttribute : ValidationAttribute 10 | { 11 | private static readonly EmailValidator _validator = new EmailValidator(); 12 | 13 | public override bool IsValid(object value) 14 | { 15 | if (value == null) 16 | { 17 | return true; // This should be handled by Required attribute 18 | } 19 | 20 | if(!(value is string)) 21 | { 22 | throw new ArgumentException($"string type required. Found {value.GetType()}", nameof(value)); 23 | } 24 | 25 | var email = value as string; 26 | return _validator.IsValid(email); 27 | } 28 | 29 | protected override ValidationResult IsValid(object value, ValidationContext validationContext) 30 | { 31 | var email = value as string; 32 | if(email != null) 33 | { 34 | var isValid = _validator.IsValid(email); 35 | if (!isValid) 36 | { 37 | return new ValidationResult($"Invalid Email: {email}"); 38 | } 39 | } 40 | 41 | return base.IsValid(value, validationContext); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /UserManager.BusinessLogic/Model/Role.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UserManager.BusinessLogic.Model 4 | { 5 | public class Role 6 | { 7 | private Role(int id, string name, string description) 8 | { 9 | Id = id; 10 | Name = name; 11 | Description = description; 12 | } 13 | 14 | public int Id { get; } 15 | 16 | public string Name { get; } 17 | 18 | public string Description { get; } 19 | 20 | public static Role Admin { get; } = new Role(1, "Admin", "Role which can perform elevated actions"); 21 | 22 | public static Role Basic { get; } = new Role(2, "Basic", "Role which can only read info"); 23 | 24 | public static Role[] Roles { get; } = new Role[] { Admin, Basic }; 25 | 26 | public static Role FromId(int id) 27 | { 28 | if (id == Admin.Id) return Admin; 29 | if (id == Basic.Id) return Basic; 30 | 31 | throw new ArgumentException($"Invalid value {id}", nameof(id)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/Model/User.cs: -------------------------------------------------------------------------------- 1 | using Dapper.Contrib.Extensions; 2 | using System; 3 | 4 | namespace UserManager.BusinessLogic.Model 5 | { 6 | [Table("[User]")] 7 | public class User 8 | { 9 | public User() 10 | { 11 | this.CreationDate = DateTimeOffset.Now; 12 | } 13 | 14 | [Key] 15 | public int Id { get; private set; } 16 | 17 | public string FirstName { get; set; } 18 | 19 | public string LastName { get; set; } 20 | 21 | public Email Email { get; set; } 22 | 23 | public Role Role { get; set; } 24 | 25 | public DateTimeOffset CreationDate { get; } 26 | } 27 | } -------------------------------------------------------------------------------- /UserManager.BusinessLogic/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UserManager.BusinessLogic")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UserManager.BusinessLogic")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ce9a2929-ed77-489b-b161-6c10fd8a5d3a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /UserManager.BusinessLogic/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/Command.cs: -------------------------------------------------------------------------------- 1 | using Ardalis.SmartEnum; 2 | using FluentMigrator.Runner; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | using System.Linq; 6 | 7 | namespace UserManager.DataAccess.Migrations 8 | { 9 | public abstract class Command : SmartEnum 10 | { 11 | private Command(string name, int value) : base(name, value) { } 12 | 13 | public static readonly Command MigrateToLatest = new MigrateToLatestCommand(); 14 | public static readonly Command Rollback = new RollbackCommand(); 15 | 16 | public static (Command Command, string[] arguments) FromString(string text) 17 | { 18 | var commandParameters = text.Split(' ').ToList(); 19 | var command = commandParameters[0]; 20 | commandParameters.RemoveAt(0); 21 | return (FromName(command), commandParameters.ToArray()); 22 | } 23 | 24 | public string Arguments { get; } 25 | 26 | public abstract void Execute(IServiceProvider serviceProvider, string[] arguments); 27 | 28 | private sealed class MigrateToLatestCommand : Command 29 | { 30 | public MigrateToLatestCommand() : base("MigrateToLatest", 1) { } 31 | 32 | public override void Execute(IServiceProvider serviceProvider, string[] arguments) 33 | { 34 | // Instantiate the runner 35 | var runner = serviceProvider.GetRequiredService(); 36 | 37 | // Execute the migrations 38 | runner.MigrateUp(); 39 | } 40 | } 41 | 42 | private class RollbackCommand : Command 43 | { 44 | public RollbackCommand() : base("Rollback", 2) { } 45 | 46 | public override void Execute(IServiceProvider serviceProvider, string[] arguments) 47 | { 48 | // Instantiate the runner 49 | var runner = serviceProvider.GetRequiredService(); 50 | 51 | // Migrate down to version 52 | var version = long.Parse(arguments[0]); 53 | runner.MigrateDown(version); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/FluentMigratorExtensions.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator.Builders.Create.Table; 2 | 3 | namespace UserManager.DataAccess.Migrations 4 | { 5 | public static class FluentMigratorExtensions 6 | { 7 | public static ICreateTableColumnOptionOrWithColumnSyntax WithIdColumn(this ICreateTableWithColumnSyntax tableWithColumnSyntax) 8 | { 9 | return tableWithColumnSyntax 10 | .WithColumn("Id") 11 | .AsInt32() 12 | .NotNullable() 13 | .PrimaryKey() 14 | .Identity(); 15 | } 16 | 17 | public static ICreateTableColumnOptionOrWithColumnSyntax WithTimeStamps(this ICreateTableWithColumnSyntax tableWithColumnSyntax) 18 | { 19 | return tableWithColumnSyntax 20 | .WithColumn("CreatedAt").AsDateTime().NotNullable() 21 | .WithColumn("ModifiedAt").AsDateTime().NotNullable(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/Migrations/20201024202704_InitialMigration.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator; 2 | 3 | namespace UserManager.DataAccess.Migrations.Migrations 4 | { 5 | [Migration(20201024202704)] 6 | public class InitialMigration : Migration 7 | { 8 | public override void Up() 9 | { 10 | 11 | } 12 | 13 | public override void Down() 14 | { 15 | 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/Migrations/20201024202825_AddUserTable.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator; 2 | 3 | namespace UserManager.DataAccess.Migrations.Migrations 4 | { 5 | [Migration(20201024202825)] 6 | public class AddUserTable : Migration 7 | { 8 | public override void Up() 9 | { 10 | Create.Table("User") 11 | .WithIdColumn() 12 | .WithColumn("FirstName").AsString(50).NotNullable() 13 | .WithColumn("LastName").AsString(100).NotNullable() 14 | .WithColumn("Email").AsString(50).NotNullable(); 15 | } 16 | 17 | public override void Down() 18 | { 19 | Delete.Table("User"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/Migrations/20201113194828_AddRoleToUser.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator; 2 | using UserManager.BusinessLogic.Model; 3 | 4 | namespace UserManager.DataAccess.Migrations.Migrations 5 | { 6 | [Migration(20201113194828)] 7 | public class AddRoleToUser : Migration 8 | { 9 | public override void Up() 10 | { 11 | Alter.Table("User") 12 | .AddColumn("Role").AsInt16().WithDefaultValue(Role.Basic.Id); 13 | } 14 | 15 | public override void Down() 16 | { 17 | Delete.Column("Role") 18 | .FromTable("User"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/Migrations/20210114223404_AddCreationDateToUser.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator; 2 | 3 | namespace UserManager.DataAccess.Migrations.Migrations 4 | { 5 | [Migration(20210114223404)] 6 | public class AddCreationDateToUser : Migration 7 | { 8 | public override void Up() 9 | { 10 | Alter.Table("User") 11 | .AddColumn("CreationDate").AsDateTimeOffset().Nullable(); 12 | 13 | Update.Table("User").Set(new { CreationDate = RawSql.Insert("SYSDATETIMEOFFSET()") }).AllRows(); 14 | 15 | Alter.Table("User") 16 | .AlterColumn("CreationDate").AsDateTimeOffset().NotNullable(); 17 | } 18 | 19 | public override void Down() 20 | { 21 | Delete.Column("CreationDate") 22 | .FromTable("User"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /UserManager.DataAccess.Migrations/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UserManager.DataAccess.Migrations")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UserManager.DataAccess.Migrations")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2cb3fa12-fa7c-4a07-8475-8f7ccec282d2")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /UserManager.IntegrationTests/Main/MainMenuTests.cs: -------------------------------------------------------------------------------- 1 | using FlaUI.Core.AutomationElements; 2 | using FluentAssertions; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using WinformsTools.IntegrationTestUtils.Extensions; 5 | using WinformsTools.IntegrationTestUtils.Elements; 6 | using UserManager.IntegrationTests.TestUtils; 7 | using UserManager.Resources; 8 | 9 | namespace UserManager.IntegrationTests.Main 10 | { 11 | [TestClass] 12 | public class MainMenuTests : TestBase 13 | { 14 | [TestMethod] 15 | public void MainView_only_allows_to_open_one_windows_type_at_the_same_time() 16 | { 17 | var mainWindow = App.GetMainWindow(); 18 | var gotoUsersButton = mainWindow.Get