├── .github └── FUNDING.yml ├── .gitignore ├── AspnetRunAngularRealWorld.sln ├── LICENSE ├── README.md ├── src ├── AspnetRun.Api │ ├── Application │ │ ├── Commands │ │ │ ├── CreateProductCommandHandler.cs │ │ │ ├── DeleteProductByIdCommandHandler.cs │ │ │ └── UpdateProductCommandHandler.cs │ │ ├── Middlewares │ │ │ └── LoggingMiddleware.cs │ │ └── Validations │ │ │ ├── CreateProductRequestValidator.cs │ │ │ ├── DeleteProductRequestValidator.cs │ │ │ ├── GetProductByIdRequestValidator.cs │ │ │ ├── GetProductsByCategoryIdRequestValidator.cs │ │ │ ├── GetProductsByNameRequestValidator.cs │ │ │ ├── SearchProductsRequestValidator.cs │ │ │ └── UpdateProductRequestValidator.cs │ ├── AspnetRun.Api.csproj │ ├── Controllers │ │ ├── AccountController.cs │ │ ├── CategoryController.cs │ │ ├── CustomerController.cs │ │ └── ProductController.cs │ ├── IoC │ │ └── DependencyRegistrar.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Requests │ │ ├── CreateProductRequest.cs │ │ ├── DeleteProductByIdRequest.cs │ │ ├── GetCustomerByIdRequest.cs │ │ ├── GetProductByIdRequest.cs │ │ ├── GetProductsByCategoryIdRequest.cs │ │ ├── GetProductsByNameRequest.cs │ │ ├── LoginRequest.cs │ │ ├── SearchPageRequest.cs │ │ └── UpdateProductRequest.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── AspnetRun.Application │ ├── AspnetRun.Application.csproj │ ├── Exceptions │ │ └── ApplicationException.cs │ ├── Interfaces │ │ ├── ICategoryService.cs │ │ ├── ICustomerService.cs │ │ └── IProductService.cs │ ├── IoC │ │ └── DependencyRegistrar.cs │ ├── Mapper │ │ └── ObjectMapper.cs │ ├── Models │ │ ├── Base │ │ │ ├── BaseModel.cs │ │ │ └── EnumModel.cs │ │ ├── CategoryModel.cs │ │ ├── CustomerModel.cs │ │ └── ProductModel.cs │ └── Services │ │ ├── CategoryService.cs │ │ ├── CustomerService.cs │ │ └── ProductService.cs ├── AspnetRun.Core │ ├── AspnetRun.Core.csproj │ ├── Configuration │ │ └── AspnetRunSettings.cs │ ├── Entities │ │ ├── Address.cs │ │ ├── AspnetRunRole.cs │ │ ├── AspnetRunUser.cs │ │ ├── Base │ │ │ ├── Entity.cs │ │ │ ├── EntityBase.cs │ │ │ ├── Enumeration.cs │ │ │ └── IEntityBase.cs │ │ ├── Category.cs │ │ ├── Contract.cs │ │ ├── ContractItem.cs │ │ ├── ContractPaymentAssociation.cs │ │ ├── Customer.cs │ │ ├── Order.cs │ │ ├── OrderItem.cs │ │ ├── OrderPaymentAssociation.cs │ │ ├── Payment.cs │ │ ├── PaymentItem.cs │ │ ├── Product.cs │ │ ├── ProductSpecificationAssociation.cs │ │ └── Specification.cs │ ├── Exceptions │ │ └── CoreException.cs │ ├── Interfaces │ │ └── IAppLogger.cs │ ├── Paging │ │ ├── FilteringOption.cs │ │ ├── IPagedList.cs │ │ ├── PageSearchArgs.cs │ │ ├── PagingArgs.cs │ │ ├── PagingStrategy.cs │ │ └── SortingOption.cs │ ├── Repositories │ │ ├── Base │ │ │ ├── IEnumRepository.cs │ │ │ ├── IRepository.cs │ │ │ └── IRepositoryBase.cs │ │ ├── ICategoryRepository.cs │ │ └── IProductRepository.cs │ └── Specifications │ │ ├── Base │ │ ├── BaseSpecification.cs │ │ └── ISpecification.cs │ │ └── ProductWithCategorySpecification.cs ├── AspnetRun.Infrastructure │ ├── AspnetRun.Infrastructure.csproj │ ├── Behaviors │ │ └── TransactionBehaviour.cs │ ├── Data │ │ ├── AspnetRunContext.cs │ │ ├── AspnetRunContextSeed.cs │ │ ├── AspnetRunCustomModelBuilder.cs │ │ └── ICustomModelBuilder.cs │ ├── Exceptions │ │ └── InfrastructureException.cs │ ├── IoC │ │ ├── DependencyRegistrar.cs │ │ └── IDependencyRegistrar.cs │ ├── Logging │ │ └── LoggerAdapter.cs │ ├── Migrations │ │ ├── 20190710081402_initial.Designer.cs │ │ ├── 20190710081402_initial.cs │ │ └── AspnetRunContextModelSnapshot.cs │ ├── Misc │ │ ├── AppDomainTypeFinder.cs │ │ ├── AppFileProvider.cs │ │ ├── IAppFileProvider.cs │ │ ├── ITypeFinder.cs │ │ └── WebAppTypeFinder.cs │ ├── Paging │ │ ├── PagedList.cs │ │ └── PagingExtensions.cs │ └── Repository │ │ ├── Base │ │ ├── EnumRepository.cs │ │ ├── Repository.cs │ │ ├── RepositoryBase.cs │ │ └── SpecificationEvaluator.cs │ │ ├── CategoryRepository.cs │ │ └── ProductRepository.cs └── AspnetRun.Web │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── browserslist │ ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.e2e.json │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── _nav.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── core │ │ │ ├── core.module.ts │ │ │ ├── ensure-module-loaded-once.guard.ts │ │ │ ├── interceptors │ │ │ │ ├── http-auth.interceptor.ts │ │ │ │ └── http-error.interceptor.ts │ │ │ ├── layout │ │ │ │ ├── layout.component.html │ │ │ │ └── layout.component.ts │ │ │ └── services │ │ │ │ ├── auth-guard.service.ts │ │ │ │ ├── auth.service.ts │ │ │ │ ├── category-data.service.ts │ │ │ │ ├── customer-data.services.ts │ │ │ │ ├── logger.service.ts │ │ │ │ ├── page.service.ts │ │ │ │ ├── product-data.service.ts │ │ │ │ ├── spinner.service.ts │ │ │ │ └── validation.service.ts │ │ ├── shared │ │ │ ├── interfaces.ts │ │ │ ├── pipes │ │ │ │ ├── capitalize.pipe.ts │ │ │ │ └── trim.pipe.ts │ │ │ ├── shared.module.ts │ │ │ └── validation-message │ │ │ │ └── validation-message.component.ts │ │ └── views │ │ │ ├── about │ │ │ ├── about.component.css │ │ │ ├── about.component.html │ │ │ └── about.component.ts │ │ │ ├── category │ │ │ ├── category-list │ │ │ │ ├── category-list.component.css │ │ │ │ ├── category-list.component.html │ │ │ │ └── category-list.component.ts │ │ │ ├── category-routing.module.ts │ │ │ └── category.module.ts │ │ │ ├── customer │ │ │ ├── customer-cart │ │ │ │ ├── customer-cart.component.css │ │ │ │ ├── customer-cart.component.html │ │ │ │ └── customer-cart.component.ts │ │ │ ├── customer-detail │ │ │ │ ├── customer-detail.component.css │ │ │ │ ├── customer-detail.component.html │ │ │ │ └── customer-detail.component.ts │ │ │ ├── customer-edit │ │ │ │ ├── customer-edit.component.css │ │ │ │ ├── customer-edit.component.html │ │ │ │ └── customer-edit.component.ts │ │ │ ├── customer-list │ │ │ │ ├── customer-grid │ │ │ │ │ ├── customer-grid.component.css │ │ │ │ │ ├── customer-grid.component.html │ │ │ │ │ └── customer-grid.component.ts │ │ │ │ ├── customer-list.component.css │ │ │ │ ├── customer-list.component.html │ │ │ │ ├── customer-list.component.ts │ │ │ │ └── filter-textbox │ │ │ │ │ ├── filter-textbox.component.css │ │ │ │ │ ├── filter-textbox.component.html │ │ │ │ │ └── filter-textbox.component.ts │ │ │ ├── customer-order │ │ │ │ ├── customer-order.component.css │ │ │ │ ├── customer-order.component.html │ │ │ │ └── customer-order.component.ts │ │ │ ├── customer-routing.module.ts │ │ │ └── customer.module.ts │ │ │ ├── dashboard │ │ │ ├── dashboard.component.html │ │ │ └── dashboard.component.ts │ │ │ ├── error │ │ │ ├── 404.component.html │ │ │ ├── 404.component.ts │ │ │ ├── 500.component.html │ │ │ └── 500.component.ts │ │ │ ├── login │ │ │ ├── login.component.html │ │ │ └── login.component.ts │ │ │ ├── product │ │ │ ├── product-delete │ │ │ │ ├── product-delete-modal.component.css │ │ │ │ ├── product-delete-modal.component.html │ │ │ │ └── product-delete-modal.component.ts │ │ │ ├── product-detail │ │ │ │ ├── product-detail.component.css │ │ │ │ ├── product-detail.component.html │ │ │ │ └── product-detail.component.ts │ │ │ ├── product-edit │ │ │ │ ├── product-edit.component.css │ │ │ │ ├── product-edit.component.html │ │ │ │ └── product-edit.component.ts │ │ │ ├── product-list │ │ │ │ ├── product-list.component.css │ │ │ │ ├── product-list.component.html │ │ │ │ └── product-list.component.ts │ │ │ ├── product-routing.module.ts │ │ │ └── product.module.ts │ │ │ └── register │ │ │ ├── register.component.html │ │ │ └── register.component.ts │ ├── assets │ │ ├── .gitkeep │ │ └── img │ │ │ ├── avatars │ │ │ └── avatar.svg │ │ │ ├── brand │ │ │ ├── logo.svg │ │ │ └── sygnet.svg │ │ │ ├── female.png │ │ │ ├── male.png │ │ │ └── people.png │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── karma.conf.js │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── tslint.json │ ├── tsconfig.json │ └── tslint.json └── test └── AspnetRun.Tests ├── AspnetRun.Tests.csproj └── UnitTest1.cs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://aspnetrun.azurewebsites.net/DownloadBook"] 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 aspnetrun 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 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Commands/CreateProductCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using AspnetRun.Api.Requests; 4 | using AspnetRun.Application.Interfaces; 5 | using AspnetRun.Application.Models; 6 | using MediatR; 7 | 8 | namespace AspnetRun.Api.Application.Commands 9 | { 10 | public class CreateProductCommandHandler 11 | : IRequestHandler 12 | { 13 | private readonly IProductService _productService; 14 | 15 | public CreateProductCommandHandler(IProductService productService) 16 | { 17 | _productService = productService; 18 | } 19 | 20 | public async Task Handle(CreateProductRequest request, CancellationToken cancellationToken) 21 | { 22 | var productModel = await _productService.CreateProduct(request.Product); 23 | 24 | return productModel; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Commands/DeleteProductByIdCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using AspnetRun.Api.Requests; 4 | using AspnetRun.Application.Interfaces; 5 | using MediatR; 6 | 7 | namespace AspnetRun.Api.Application.Commands 8 | { 9 | public class DeleteProductByIdCommandHandler : IRequestHandler 10 | { 11 | private readonly IProductService _productService; 12 | 13 | public DeleteProductByIdCommandHandler(IProductService productService) 14 | { 15 | _productService = productService; 16 | } 17 | 18 | public async Task Handle(DeleteProductByIdRequest request, CancellationToken cancellationToken) 19 | { 20 | await _productService.DeleteProductById(request.Id); 21 | 22 | return Unit.Value; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Commands/UpdateProductCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using AspnetRun.Application.Interfaces; 3 | using MediatR; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace AspnetRun.Api.Application.Commands 8 | { 9 | public class UpdateProductCommandHandler : IRequestHandler 10 | { 11 | private readonly IProductService _productService; 12 | 13 | public UpdateProductCommandHandler(IProductService productService) 14 | { 15 | _productService = productService; 16 | } 17 | 18 | public async Task Handle(UpdateProductRequest request, CancellationToken cancellationToken) 19 | { 20 | await _productService.UpdateProduct(request.Product); 21 | 22 | return Unit.Value; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Middlewares/LoggingMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.Logging; 3 | using System.Threading.Tasks; 4 | 5 | namespace AspnetRun.Api.Application.Middlewares 6 | { 7 | public class LoggingMiddleware 8 | { 9 | private readonly RequestDelegate _next; 10 | private readonly ILogger _logger; 11 | 12 | public LoggingMiddleware(RequestDelegate next, ILogger logger) 13 | { 14 | _next = next; 15 | _logger = logger; 16 | } 17 | 18 | public async Task Invoke(HttpContext context) 19 | { 20 | _logger.LogInformation("Before request"); 21 | await _next(context); 22 | _logger.LogInformation("After request"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Validations/CreateProductRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using FluentValidation; 3 | 4 | namespace AspnetRun.Api.Application.Validations 5 | { 6 | public class CreateProductRequestValidator : AbstractValidator 7 | { 8 | public CreateProductRequestValidator() 9 | { 10 | RuleFor(request => request.Product).NotNull(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Validations/DeleteProductRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using FluentValidation; 3 | 4 | namespace AspnetRun.Api.Application.Validations 5 | { 6 | public class DeleteProductRequestValidator : AbstractValidator 7 | { 8 | public DeleteProductRequestValidator() 9 | { 10 | RuleFor(request => request.Id).GreaterThan(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Validations/GetProductByIdRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using FluentValidation; 3 | 4 | namespace AspnetRun.Api.Application.Validations 5 | { 6 | public class GetProductByIdRequestValidator : AbstractValidator 7 | { 8 | public GetProductByIdRequestValidator() 9 | { 10 | RuleFor(request => request.Id).GreaterThan(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Validations/GetProductsByCategoryIdRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using FluentValidation; 3 | 4 | namespace AspnetRun.Api.Application.Validations 5 | { 6 | public class GetProductsByCategoryIdRequestValidator : AbstractValidator 7 | { 8 | public GetProductsByCategoryIdRequestValidator() 9 | { 10 | RuleFor(request => request.CategoryId).GreaterThan(0); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Validations/GetProductsByNameRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using FluentValidation; 3 | 4 | namespace AspnetRun.Api.Application.Validations 5 | { 6 | public class GetProductsByNameRequestValidator : AbstractValidator 7 | { 8 | public GetProductsByNameRequestValidator() 9 | { 10 | RuleFor(request => request.Name).NotNull(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Validations/SearchProductsRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using FluentValidation; 3 | 4 | namespace AspnetRun.Api.Application.Validations 5 | { 6 | public class SearchProductsRequestValidator : AbstractValidator 7 | { 8 | public SearchProductsRequestValidator() 9 | { 10 | RuleFor(request => request.Args).NotNull(); 11 | RuleFor(request => request.Args.PageIndex).GreaterThan(0); 12 | RuleFor(request => request.Args.PageSize).InclusiveBetween(10, 100); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Application/Validations/UpdateProductRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using FluentValidation; 3 | 4 | namespace AspnetRun.Api.Application.Validations 5 | { 6 | public class UpdateProductRequestValidator : AbstractValidator 7 | { 8 | public UpdateProductRequestValidator() 9 | { 10 | RuleFor(request => request.Product).NotNull(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/AspnetRun.Api.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.2 5 | InProcess 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using AspnetRun.Core.Configuration; 3 | using AspnetRun.Core.Entities; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Options; 7 | using Microsoft.IdentityModel.Tokens; 8 | using System; 9 | using System.IdentityModel.Tokens.Jwt; 10 | using System.Security.Claims; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace AspnetRun.Api.Controllers 15 | { 16 | [Route("api/[controller]")] 17 | [ApiController] 18 | public class AccountController : ControllerBase 19 | { 20 | private readonly AspnetRunSettings _aspnetRunSettings; 21 | private readonly SignInManager _signInManager; 22 | private readonly UserManager _userManager; 23 | 24 | public AccountController(SignInManager signInManager, 25 | UserManager userManager, 26 | IOptions options) 27 | { 28 | _signInManager = signInManager; 29 | _userManager = userManager; 30 | _aspnetRunSettings = options.Value; 31 | } 32 | 33 | [Route("[action]")] 34 | [HttpPost] 35 | public async Task CreateToken([FromBody]LoginRequest request) 36 | { 37 | var user = await _userManager.FindByEmailAsync(request.Email); 38 | 39 | if (user != null) 40 | { 41 | var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, false); 42 | 43 | if (result.Succeeded) 44 | { 45 | // Create the token 46 | var claims = new[] 47 | { 48 | new Claim(JwtRegisteredClaimNames.Sub, user.Email), 49 | new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), 50 | new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName) 51 | }; 52 | 53 | var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_aspnetRunSettings.Tokens.Key)); 54 | var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); 55 | 56 | var token = new JwtSecurityToken( 57 | _aspnetRunSettings.Tokens.Issuer, 58 | _aspnetRunSettings.Tokens.Audience, 59 | claims, 60 | expires: DateTime.Now.AddMinutes(30), 61 | signingCredentials: signingCredentials); 62 | 63 | var results = new 64 | { 65 | token = new JwtSecurityTokenHandler().WriteToken(token), 66 | expiration = token.ValidTo 67 | }; 68 | 69 | return Created("", results); 70 | } 71 | } 72 | 73 | return Unauthorized(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Controllers/CategoryController.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using AspnetRun.Application.Interfaces; 3 | using AspnetRun.Application.Models; 4 | using AspnetRun.Core.Paging; 5 | using MediatR; 6 | using Microsoft.AspNetCore.Authentication.JwtBearer; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.AspNetCore.Mvc; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Net; 12 | using System.Threading.Tasks; 13 | 14 | namespace AspnetRun.Api.Controllers 15 | { 16 | [Route("api/[controller]")] 17 | [ApiController] 18 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 19 | public class CategoryController : ControllerBase 20 | { 21 | private readonly IMediator _mediator; 22 | private readonly ICategoryService _categoryService; 23 | 24 | public CategoryController(IMediator mediator, ICategoryService categoryService) 25 | { 26 | _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); 27 | _categoryService = categoryService ?? throw new ArgumentNullException(nameof(categoryService)); 28 | } 29 | 30 | [Route("[action]")] 31 | [HttpGet] 32 | [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] 33 | public async Task>> GetCategories() 34 | { 35 | var categories = await _categoryService.GetCategoryList(); 36 | 37 | return Ok(categories); 38 | } 39 | 40 | [Route("[action]")] 41 | [HttpPost] 42 | [ProducesResponseType(typeof(IPagedList), (int)HttpStatusCode.OK)] 43 | public async Task>> SearchCategories(SearchPageRequest request) 44 | { 45 | var categoryPagedList = await _categoryService.SearchCategories(request.Args); 46 | 47 | return Ok(categoryPagedList); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Controllers/CustomerController.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Requests; 2 | using AspnetRun.Application.Interfaces; 3 | using AspnetRun.Application.Models; 4 | using MediatR; 5 | using Microsoft.AspNetCore.Authentication.JwtBearer; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Net; 12 | using System.Threading.Tasks; 13 | 14 | namespace AspnetRun.Api.Controllers 15 | { 16 | [Route("api/[controller]")] 17 | [ApiController] 18 | [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] 19 | public class CustomerController : ControllerBase 20 | { 21 | private readonly IMediator _mediator; 22 | private readonly ICustomerService _customerService; 23 | 24 | public CustomerController(IMediator mediator, ICustomerService customerService) 25 | { 26 | _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); 27 | _customerService = customerService ?? throw new ArgumentNullException(nameof(customerService)); 28 | } 29 | 30 | [Route("[action]")] 31 | [HttpGet] 32 | [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] 33 | public async Task>> GetCustomers() 34 | { 35 | //var categories = await _customerService.GetCustomerList(); 36 | 37 | var customers = new List 38 | { 39 | new CustomerModel 40 | { 41 | Id = 1, 42 | FirstName = "mehmet", 43 | LastName = "ozkaya", 44 | Address = "gungoren", 45 | City = "istanbul", 46 | Description = "asdf", 47 | State = "success", 48 | Gender = "male", 49 | OrderTotal = 22.2 50 | }, 51 | new CustomerModel 52 | { 53 | Id = 2, 54 | FirstName = "merve", 55 | LastName = "ozkaya", 56 | Address = "gungoren", 57 | City = "istanbul", 58 | Description = "asdf", 59 | State = "success", 60 | Gender = "female", 61 | OrderTotal = 22.2 62 | }, 63 | new CustomerModel 64 | { 65 | Id = 3, 66 | FirstName = "zeynep", 67 | LastName = "ozkaya", 68 | Address = "gungoren", 69 | City = "istanbul", 70 | Description = "asdf", 71 | State = "success", 72 | Gender = "female", 73 | OrderTotal = 22.2 74 | }, 75 | }; 76 | 77 | return Ok(customers); 78 | } 79 | 80 | [Route("[action]")] 81 | [HttpPost] 82 | [ProducesResponseType(typeof(CustomerModel), (int)HttpStatusCode.OK)] 83 | public async Task> GetCustomerById(GetCustomerByIdRequest request) 84 | { 85 | //var product = await _productService.GetProductById(request.Id); 86 | 87 | var customer = new CustomerModel 88 | { 89 | Id = 1, 90 | FirstName = "mehmet", 91 | LastName = "ozkaya", 92 | Address = "gungoren", 93 | City = "istanbul", 94 | Description = "asdf", 95 | State = "success", 96 | Gender = "male", 97 | OrderTotal = 22.2 98 | }; 99 | 100 | return Ok(customer); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/IoC/DependencyRegistrar.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Api.Application.Commands; 2 | using AspnetRun.Api.Application.Validations; 3 | using AspnetRun.Infrastructure.IoC; 4 | using AspnetRun.Infrastructure.Misc; 5 | using Autofac; 6 | using FluentValidation; 7 | using MediatR; 8 | using System.Reflection; 9 | 10 | namespace AspnetRun.Api.IoC 11 | { 12 | public class DependencyRegistrar : IDependencyRegistrar 13 | { 14 | public void Register(ContainerBuilder builder, ITypeFinder typeFinder) 15 | { 16 | // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands 17 | builder.RegisterAssemblyTypes(typeof(CreateProductCommandHandler).GetTypeInfo().Assembly) 18 | .AsClosedTypesOf(typeof(IRequestHandler<,>)); 19 | 20 | // Register the Command's Validators (Validators based on FluentValidation library) 21 | builder.RegisterAssemblyTypes(typeof(CreateProductRequestValidator).GetTypeInfo().Assembly) 22 | .Where(t => t.IsClosedTypeOf(typeof(IValidator<>))) 23 | .AsImplementedInterfaces(); 24 | } 25 | 26 | public int Order => 0; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Infrastructure.Data; 2 | using Microsoft.AspNetCore; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | 8 | namespace AspnetRun.Api 9 | { 10 | public class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | var host = CreateWebHostBuilder(args) 15 | .Build(); 16 | 17 | using (var scope = host.Services.CreateScope()) 18 | { 19 | var services = scope.ServiceProvider; 20 | var loggerFactory = services.GetRequiredService(); 21 | try 22 | { 23 | var aspnetRunContextSeed = services.GetRequiredService(); 24 | aspnetRunContextSeed.SeedAsync().Wait(); 25 | } 26 | catch (Exception ex) 27 | { 28 | var logger = loggerFactory.CreateLogger(); 29 | logger.LogError(ex, "An error occurred seeding the DB."); 30 | } 31 | } 32 | 33 | host.Run(); 34 | } 35 | 36 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 37 | WebHost.CreateDefaultBuilder(args) 38 | .UseStartup(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:64167/", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "launchUrl": "swagger", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "AspnetRun.Api": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "applicationUrl": "http://localhost:64168/" 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/CreateProductRequest.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models; 2 | using MediatR; 3 | 4 | namespace AspnetRun.Api.Requests 5 | { 6 | public class CreateProductRequest : IRequest 7 | { 8 | public ProductModel Product { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/DeleteProductByIdRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace AspnetRun.Api.Requests 4 | { 5 | public class DeleteProductByIdRequest : IRequest 6 | { 7 | public int Id { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/GetCustomerByIdRequest.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Api.Requests 2 | { 3 | public class GetCustomerByIdRequest 4 | { 5 | public int Id { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/GetProductByIdRequest.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Api.Requests 2 | { 3 | public class GetProductByIdRequest 4 | { 5 | public int Id { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/GetProductsByCategoryIdRequest.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Api.Requests 2 | { 3 | public class GetProductsByCategoryIdRequest 4 | { 5 | public int CategoryId { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/GetProductsByNameRequest.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Api.Requests 2 | { 3 | public class GetProductsByNameRequest 4 | { 5 | public string Name { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/LoginRequest.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Api.Requests 2 | { 3 | public class LoginRequest 4 | { 5 | public string Email { get; set; } 6 | public string Password { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/SearchPageRequest.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Paging; 2 | 3 | namespace AspnetRun.Api.Requests 4 | { 5 | public class SearchPageRequest 6 | { 7 | public PageSearchArgs Args { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/Requests/UpdateProductRequest.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models; 2 | using MediatR; 3 | 4 | namespace AspnetRun.Api.Requests 5 | { 6 | public class UpdateProductRequest : IRequest 7 | { 8 | public ProductModel Product { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionString": "Server=(localdb)\\mssqllocaldb;Integrated Security=true;Initial Catalog=AspnetRunAngularRealWorld;", 3 | "Tokens": { 4 | "Key": "a1s2d3f4g5h5*-*0*897867545,i.l!!!!", 5 | "Issuer": "http://localhost:64167", 6 | "Audience": "expertise" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Debug", 11 | "System": "Information", 12 | "Microsoft": "Information" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/AspnetRun.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/AspnetRun.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Exceptions/ApplicationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspnetRun.Application.Exceptions 4 | { 5 | public class ApplicationException : Exception 6 | { 7 | internal ApplicationException() 8 | { 9 | } 10 | 11 | internal ApplicationException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | internal ApplicationException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Interfaces/ICategoryService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models; 2 | using AspnetRun.Core.Paging; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace AspnetRun.Application.Interfaces 7 | { 8 | public interface ICategoryService 9 | { 10 | Task> GetCategoryList(); 11 | 12 | Task> SearchCategories(PageSearchArgs args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Interfaces/ICustomerService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace AspnetRun.Application.Interfaces 8 | { 9 | public interface ICustomerService 10 | { 11 | Task> GetCustomerList(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Interfaces/IProductService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models; 2 | using AspnetRun.Core.Paging; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace AspnetRun.Application.Interfaces 7 | { 8 | public interface IProductService 9 | { 10 | Task> GetProductList(); 11 | Task> SearchProducts(PageSearchArgs args); 12 | Task GetProductById(int productId); 13 | Task> GetProductsByName(string name); 14 | Task> GetProductsByCategoryId(int categoryId); 15 | Task CreateProduct(ProductModel product); 16 | Task UpdateProduct(ProductModel product); 17 | Task DeleteProductById(int productId); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/IoC/DependencyRegistrar.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Interfaces; 2 | using AspnetRun.Application.Services; 3 | using AspnetRun.Infrastructure.IoC; 4 | using AspnetRun.Infrastructure.Misc; 5 | using Autofac; 6 | 7 | namespace AspnetRun.Application.IoC 8 | { 9 | public class DependencyRegistrar : IDependencyRegistrar 10 | { 11 | public void Register(ContainerBuilder builder, ITypeFinder typeFinder) 12 | { 13 | // services 14 | builder.RegisterType().As().InstancePerLifetimeScope(); 15 | builder.RegisterType().As().InstancePerLifetimeScope(); 16 | builder.RegisterType().As().InstancePerLifetimeScope(); 17 | } 18 | 19 | public int Order => 2; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Mapper/ObjectMapper.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models; 2 | using AspnetRun.Core.Entities; 3 | using AutoMapper; 4 | 5 | namespace AspnetRun.Application.Mapper 6 | { 7 | public class ObjectMapper 8 | { 9 | public static IMapper Mapper => AutoMapper.Mapper.Instance; 10 | 11 | static ObjectMapper() 12 | { 13 | CreateMap(); 14 | } 15 | 16 | private static void CreateMap() 17 | { 18 | AutoMapper.Mapper.Initialize(cfg => 19 | { 20 | cfg.CreateMap().ReverseMap(); 21 | cfg.CreateMap().ReverseMap(); 22 | }); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Models/Base/BaseModel.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Application.Models.Base 2 | { 3 | public class BaseModel 4 | { 5 | public int Id { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Models/Base/EnumModel.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Application.Models.Base 2 | { 3 | public class EnumModel 4 | { 5 | public int Id { get; set; } 6 | public string Code { get; set; } 7 | public string Name { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Models/CategoryModel.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models.Base; 2 | 3 | namespace AspnetRun.Application.Models 4 | { 5 | public class CategoryModel : BaseModel 6 | { 7 | public string Name { get; set; } 8 | public string Description { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Models/CustomerModel.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models.Base; 2 | 3 | namespace AspnetRun.Application.Models 4 | { 5 | public class CustomerModel : BaseModel 6 | { 7 | public string FirstName { get; set; } 8 | public string LastName { get; set; } 9 | public string Address { get; set; } 10 | public string City { get; set; } 11 | public string State { get; set; } 12 | public double OrderTotal { get; set; } 13 | public string Description { get; set; } 14 | public string Gender { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Models/ProductModel.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Models.Base; 2 | using System; 3 | 4 | namespace AspnetRun.Application.Models 5 | { 6 | public class ProductModel : BaseModel 7 | { 8 | public string Name { get; set; } 9 | public string Description { get; set; } 10 | public decimal? UnitPrice { get; set; } 11 | public int CategoryId { get; set; } 12 | public CategoryModel Category { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Services/CategoryService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using AspnetRun.Application.Interfaces; 5 | using AspnetRun.Application.Mapper; 6 | using AspnetRun.Application.Models; 7 | using AspnetRun.Core.Entities; 8 | using AspnetRun.Core.Interfaces; 9 | using AspnetRun.Core.Paging; 10 | using AspnetRun.Core.Repositories; 11 | using AspnetRun.Core.Repositories.Base; 12 | using AspnetRun.Infrastructure.Paging; 13 | 14 | namespace AspnetRun.Application.Services 15 | { 16 | public class CategoryService : ICategoryService 17 | { 18 | private readonly ICategoryRepository _categoryRepository; 19 | private readonly IAppLogger _logger; 20 | 21 | public CategoryService(ICategoryRepository categoryRepository, IAppLogger logger) 22 | { 23 | _categoryRepository = categoryRepository ?? throw new ArgumentNullException(nameof(categoryRepository)); 24 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 25 | } 26 | 27 | public async Task> GetCategoryList() 28 | { 29 | var categoryList = await _categoryRepository.ListAllAsync(); 30 | 31 | var categoryModels = ObjectMapper.Mapper.Map>(categoryList); 32 | 33 | return categoryModels; 34 | } 35 | 36 | public async Task> SearchCategories(PageSearchArgs args) 37 | { 38 | var categoryPagedList = await _categoryRepository.SearchCategoriesAsync(args); 39 | 40 | var categoryModels = ObjectMapper.Mapper.Map>(categoryPagedList.Items); 41 | 42 | var categoryModelPagedList = new PagedList( 43 | categoryPagedList.PageIndex, 44 | categoryPagedList.PageSize, 45 | categoryPagedList.TotalCount, 46 | categoryPagedList.TotalPages, 47 | categoryModels); 48 | 49 | return categoryModelPagedList; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/AspnetRun.Application/Services/CustomerService.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Application.Interfaces; 2 | using AspnetRun.Application.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace AspnetRun.Application.Services 8 | { 9 | public class CustomerService : ICustomerService 10 | { 11 | //private readonly IRepository _customerRepository; 12 | //private readonly IAppLogger _logger; 13 | 14 | //public CustomerService(IRepository customerRepository, IAppLogger logger) 15 | //{ 16 | // _customerRepository = customerRepository ?? throw new ArgumentNullException(nameof(customerRepository)); 17 | // _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 18 | //} 19 | 20 | //public async Task> GetCustomerList() 21 | //{ 22 | // var customerList = await _customerRepository.ListAllAsync(); 23 | 24 | // var customerModels = ObjectMapper.Mapper.Map>(customerList); 25 | 26 | // return customerModels; 27 | //} 28 | public Task> GetCustomerList() 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/AspnetRun.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Configuration/AspnetRunSettings.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Configuration 2 | { 3 | public class AspnetRunSettings 4 | { 5 | public string ConnectionString { get; set; } 6 | 7 | public Tokens Tokens { get; set; } 8 | } 9 | 10 | public class Tokens 11 | { 12 | public string Issuer { get; set; } 13 | public string Audience { get; set; } 14 | public string Key { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Address.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class Address : Entity 6 | { 7 | public string AddressTitle { get; set; } 8 | public string FirstName { get; set; } 9 | public string LastName { get; set; } 10 | public string EmailAddress { get; set; } 11 | public string PhoneNo { get; set; } 12 | public string CompanyName { get; set; } 13 | public string AddressLine { get; set; } 14 | public string Country { get; set; } 15 | public string City { get; set; } 16 | public string State { get; set; } 17 | public string ZipCode { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/AspnetRunRole.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using Microsoft.AspNetCore.Identity; 3 | 4 | namespace AspnetRun.Core.Entities 5 | { 6 | public class AspnetRunRole : IdentityRole, IEntityBase 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/AspnetRunUser.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using Microsoft.AspNetCore.Identity; 3 | using System; 4 | 5 | namespace AspnetRun.Core.Entities 6 | { 7 | public class AspnetRunUser : IdentityUser, IEntityBase 8 | { 9 | public string FirstName { get; set; } 10 | 11 | public string LastName { get; set; } 12 | 13 | public DateTime LastLoginTime { get; set; } 14 | 15 | public bool IsWorking { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Base/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Entities.Base 2 | { 3 | public abstract class Entity : EntityBase 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Base/EntityBase.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Entities.Base 2 | { 3 | public abstract class EntityBase : IEntityBase 4 | { 5 | public virtual TId Id { get; protected set; } 6 | 7 | int? _requestedHashCode; 8 | 9 | public bool IsTransient() 10 | { 11 | return Id.Equals(default(TId)); 12 | } 13 | 14 | public override bool Equals(object obj) 15 | { 16 | if (obj == null || !(obj is EntityBase)) 17 | return false; 18 | 19 | if (ReferenceEquals(this, obj)) 20 | return true; 21 | 22 | if (GetType() != obj.GetType()) 23 | return false; 24 | 25 | var item = (EntityBase)obj; 26 | 27 | if (item.IsTransient() || IsTransient()) 28 | return false; 29 | else 30 | return false;//return item == this; 31 | } 32 | 33 | public override int GetHashCode() 34 | { 35 | if (!IsTransient()) 36 | { 37 | if (!_requestedHashCode.HasValue) 38 | _requestedHashCode = Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx) 39 | 40 | return _requestedHashCode.Value; 41 | } 42 | else 43 | return base.GetHashCode(); 44 | } 45 | 46 | public static bool operator ==(EntityBase left, EntityBase right) 47 | { 48 | if (Equals(left, null)) 49 | return Equals(right, null) ? true : false; 50 | else 51 | return left.Equals(right); 52 | } 53 | 54 | public static bool operator !=(EntityBase left, EntityBase right) 55 | { 56 | return !(left == right); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Base/Enumeration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace AspnetRun.Core.Entities.Base 7 | { 8 | public abstract class Enumeration : EntityBase, IComparable 9 | { 10 | public string Code { get; protected set; } 11 | public string Name { get; protected set; } 12 | 13 | protected Enumeration() 14 | { 15 | } 16 | 17 | protected Enumeration(int id, string code, string name) 18 | { 19 | Id = id; 20 | Code = code; 21 | Name = name; 22 | } 23 | 24 | public override string ToString() => Name; 25 | 26 | public static IEnumerable GetAll() where T : Enumeration 27 | { 28 | var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); 29 | 30 | return fields.Select(f => f.GetValue(null)).Cast(); 31 | } 32 | 33 | public override bool Equals(object obj) 34 | { 35 | var otherValue = obj as Enumeration; 36 | 37 | if (otherValue == null) 38 | return false; 39 | 40 | var typeMatches = GetType().Equals(obj.GetType()); 41 | var valueMatches = Id.Equals(otherValue.Id); 42 | 43 | return typeMatches && valueMatches; 44 | } 45 | 46 | public override int GetHashCode() => Id.GetHashCode(); 47 | 48 | public static T FromValue(int value) where T : Enumeration 49 | { 50 | var matchingItem = Parse(value, "value", item => item.Id == value); 51 | return matchingItem; 52 | } 53 | 54 | public static T FromDisplayName(string displayName) where T : Enumeration 55 | { 56 | var matchingItem = Parse(displayName, "display name", item => item.Name == displayName); 57 | return matchingItem; 58 | } 59 | 60 | private static T Parse(K value, string description, Func predicate) where T : Enumeration 61 | { 62 | var matchingItem = GetAll().FirstOrDefault(predicate); 63 | 64 | if (matchingItem == null) 65 | throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}"); 66 | 67 | return matchingItem; 68 | } 69 | 70 | public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Base/IEntityBase.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Entities.Base 2 | { 3 | public interface IEntityBase 4 | { 5 | TId Id { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Category.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace AspnetRun.Core.Entities 5 | { 6 | public class Category : Entity 7 | { 8 | [Required, StringLength(80)] 9 | public string Name { get; set; } 10 | public string Description { get; set; } 11 | public string ImageName { get; set; } 12 | 13 | public static Category Create(int categoryId, string name, string description = null) 14 | { 15 | var category = new Category 16 | { 17 | Id = categoryId, 18 | Name = name, 19 | Description = description 20 | }; 21 | return category; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Contract.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using System.Collections.Generic; 3 | 4 | namespace AspnetRun.Core.Entities 5 | { 6 | public class Contract : Entity 7 | { 8 | public int CustomerId { get; set; } 9 | public Customer Customer { get; set; } 10 | public int BillingAddressId { get; set; } 11 | public Address BillingAddress { get; set; } 12 | public int ShippingAddressId { get; set; } 13 | public Address ShippingAddress { get; set; } 14 | public ContractStatus Status { get; set; } 15 | public decimal GrandTotal { get; set; } 16 | 17 | public List Items { get; set; } = new List(); 18 | 19 | // n-n relationships 20 | public IList Payments { get; set; } = new List(); 21 | } 22 | 23 | public enum ContractStatus 24 | { 25 | OnGoing = 1, 26 | Closed = 2 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/ContractItem.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class ContractItem : Entity 6 | { 7 | public int Quantity { get; set; } 8 | public decimal UnitPrice { get; set; } 9 | public decimal TotalPrice { get; set; } 10 | public int ProductId { get; set; } 11 | public Product Product { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/ContractPaymentAssociation.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class ContractPaymentAssociation : Entity 6 | { 7 | public int ContractId { get; set; } 8 | public Contract Contract { get; set; } 9 | 10 | public int PaymentId { get; set; } 11 | public Payment Payment { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Customer.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using System.Collections.Generic; 3 | 4 | namespace AspnetRun.Core.Entities 5 | { 6 | public class Customer : Entity 7 | { 8 | public string Name { get; set; } 9 | public string Surname { get; set; } 10 | public string Phone { get; set; } 11 | public int DefaultAddressId { get; set; } 12 | public Address DefaultAddress { get; set; } 13 | public string Email { get; set; } 14 | public string CitizenId { get; set; } 15 | 16 | public List
Addresses { get; set; } = new List
(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Order.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using System.Collections.Generic; 3 | 4 | namespace AspnetRun.Core.Entities 5 | { 6 | public class Order : Entity 7 | { 8 | public int CustomerId { get; set; } 9 | public Customer Customer { get; set; } 10 | public int BillingAddressId { get; set; } 11 | public Address BillingAddress { get; set; } 12 | public int ShippingAddressId { get; set; } 13 | public Address ShippingAddress { get; set; } 14 | public OrderStatus Status { get; set; } 15 | public decimal GrandTotal { get; set; } 16 | 17 | public List Items { get; set; } = new List(); 18 | 19 | // n-n relationships 20 | public IList Payments { get; set; } = new List(); 21 | } 22 | 23 | public enum OrderStatus 24 | { 25 | Draft = 1, 26 | Canceled = 2, 27 | Closed = 3 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/OrderItem.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class OrderItem : Entity 6 | { 7 | public int Quantity { get; set; } 8 | public decimal UnitPrice { get; set; } 9 | public decimal TotalPrice { get; set; } 10 | public int ProductId { get; set; } 11 | public Product Product { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/OrderPaymentAssociation.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class OrderPaymentAssociation : Entity 6 | { 7 | public int OrderId { get; set; } 8 | public Order Order { get; set; } 9 | 10 | public int PaymentId { get; set; } 11 | public Payment Payment { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Payment.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using System.Collections.Generic; 3 | 4 | namespace AspnetRun.Core.Entities 5 | { 6 | public class Payment : Entity 7 | { 8 | public decimal GrandTotal { get; set; } 9 | 10 | public List Items { get; set; } = new List(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/PaymentItem.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class PaymentItem : Entity 6 | { 7 | public decimal Amount { get; set; } 8 | 9 | public PaymentMethod Method { get; set; } 10 | } 11 | 12 | public enum PaymentMethod 13 | { 14 | Cash = 1, 15 | CreditCard = 2, 16 | Check = 3, 17 | BankTransfer = 4, 18 | Paypal = 5, 19 | Payoneer = 6 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Product.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace AspnetRun.Core.Entities 6 | { 7 | public class Product : Entity 8 | { 9 | [Required, StringLength(80)] 10 | public string Name { get; set; } 11 | public string Slug { get; set; } 12 | public string Summary { get; set; } 13 | public string Description { get; set; } 14 | public string ImageFile { get; set; } 15 | public decimal? UnitPrice { get; set; } 16 | public int? UnitsInStock { get; set; } 17 | public double Star { get; set; } 18 | 19 | // n-1 relationships 20 | public int CategoryId { get; set; } 21 | public virtual Category Category { get; set; } 22 | 23 | // n-n relationships 24 | public IList Specifications { get; set; } = new List(); 25 | 26 | public static Product Create(int productId, int categoryId, string name, decimal unitPrice = 0, short? unitsInStock = null) 27 | { 28 | var product = new Product 29 | { 30 | Id = productId, 31 | CategoryId = categoryId, 32 | Name = name, 33 | UnitPrice = unitPrice, 34 | UnitsInStock = unitsInStock 35 | }; 36 | return product; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/ProductSpecificationAssociation.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class ProductSpecificationAssociation : Entity 6 | { 7 | public int ProductId { get; set; } 8 | public Product Product { get; set; } 9 | 10 | public int SpecificationId { get; set; } 11 | public Specification Specification { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Entities/Specification.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Entities 4 | { 5 | public class Specification : Entity 6 | { 7 | public string Name { get; set; } 8 | public string Description { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Exceptions/CoreException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspnetRun.Core.Exceptions 4 | { 5 | public class CoreException : Exception 6 | { 7 | internal CoreException() 8 | { 9 | } 10 | 11 | internal CoreException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | internal CoreException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Interfaces/IAppLogger.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Interfaces 2 | { 3 | public interface IAppLogger 4 | { 5 | void LogInformation(string message, params object[] args); 6 | void LogWarning(string message, params object[] args); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Paging/FilteringOption.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Paging 2 | { 3 | public class FilteringOption 4 | { 5 | public string Field { get; set; } 6 | 7 | public FilteringOperator Operator { get; set; } 8 | 9 | public object Value { get; set; } 10 | 11 | public enum FilteringOperator 12 | { 13 | //Empty, 14 | Contains, 15 | Not_Contains, 16 | LT, 17 | LE, 18 | GT, 19 | GE, 20 | NE, 21 | EQ, 22 | StartsWith, 23 | EndsWith, 24 | RangeInclusive, 25 | RangeExclusive, 26 | IN, 27 | NOT_IN, 28 | IN_CONTAINS, 29 | NOT_IN_CONTAINS 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Paging/IPagedList.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AspnetRun.Core.Paging 4 | { 5 | public interface IPagedList 6 | { 7 | /// 8 | /// Page index 9 | /// 10 | int PageIndex { get; } 11 | 12 | /// 13 | /// Page size 14 | /// 15 | int PageSize { get; } 16 | 17 | /// 18 | /// Total count 19 | /// 20 | int TotalCount { get; } 21 | 22 | /// 23 | /// Total pages 24 | /// 25 | int TotalPages { get; } 26 | 27 | /// 28 | /// Has previous page 29 | /// 30 | bool HasPreviousPage { get; } 31 | 32 | /// 33 | /// Has next age 34 | /// 35 | bool HasNextPage { get; } 36 | 37 | /// 38 | /// Items 39 | /// 40 | IEnumerable Items { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Paging/PageSearchArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AspnetRun.Core.Paging 4 | { 5 | public class PageSearchArgs 6 | { 7 | /// 8 | /// Page index 9 | /// 10 | public int PageIndex { get; set; } 11 | 12 | /// 13 | /// Page size 14 | /// 15 | public int PageSize { get; set; } 16 | 17 | /// 18 | /// Paging strategy 19 | /// 20 | public PagingStrategy PagingStrategy { get; set; } 21 | 22 | /// 23 | /// Sorting options 24 | /// 25 | public List SortingOptions { get; set; } 26 | 27 | /// 28 | /// Filtering options 29 | /// 30 | public List FilteringOptions { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Paging/PagingArgs.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Paging 2 | { 3 | public class PagingArgs 4 | { 5 | public int PageIndex { get; set; } 6 | 7 | public int PageSize { get; set; } 8 | 9 | public PagingStrategy PagingStrategy { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Paging/PagingStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Paging 2 | { 3 | public enum PagingStrategy 4 | { 5 | WithCount = 0, 6 | NoCount = 1 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Paging/SortingOption.cs: -------------------------------------------------------------------------------- 1 | namespace AspnetRun.Core.Paging 2 | { 3 | public class SortingOption 4 | { 5 | public string Field { get; set; } 6 | 7 | public SortingDirection Direction { get; set; } 8 | 9 | public int Priority { get; set; } 10 | 11 | public enum SortingDirection 12 | { 13 | ASC, 14 | DESC 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Repositories/Base/IEnumRepository.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Repositories.Base 4 | { 5 | public interface IEnumRepository : IRepositoryBase where T : IEntityBase 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Repositories/Base/IRepository.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | 3 | namespace AspnetRun.Core.Repositories.Base 4 | { 5 | public interface IRepository : IRepositoryBase where T : IEntityBase 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Repositories/Base/IRepositoryBase.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using AspnetRun.Core.Specifications.Base; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace AspnetRun.Core.Repositories.Base 10 | { 11 | public interface IRepositoryBase where T : IEntityBase 12 | { 13 | IQueryable Table { get; } 14 | 15 | IQueryable TableNoTracking { get; } 16 | 17 | Task GetByIdAsync(TId id); 18 | 19 | Task> ListAllAsync(); 20 | 21 | Task> AddRangeAsync(IEnumerable entities); 22 | 23 | Task SaveAsync(T entity); 24 | 25 | Task DeleteAsync(T entity); 26 | 27 | Task> GetAsync(ISpecification spec); 28 | 29 | Task> GetAsync(Expression> predicate); 30 | 31 | Task> GetAsync(Expression> predicate = null, 32 | Func, IOrderedQueryable> orderBy = null, 33 | string includeString = null, 34 | bool disableTracking = true); 35 | 36 | Task> GetAsync(Expression> predicate = null, 37 | Func, IOrderedQueryable> orderBy = null, 38 | List>> includes = null, 39 | bool disableTracking = true); 40 | 41 | Task CountAsync(ISpecification spec); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Repositories/ICategoryRepository.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities; 2 | using AspnetRun.Core.Paging; 3 | using AspnetRun.Core.Repositories.Base; 4 | using System.Threading.Tasks; 5 | 6 | namespace AspnetRun.Core.Repositories 7 | { 8 | public interface ICategoryRepository : IRepository 9 | { 10 | Task> SearchCategoriesAsync(PageSearchArgs args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Repositories/IProductRepository.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities; 2 | using AspnetRun.Core.Paging; 3 | using AspnetRun.Core.Repositories.Base; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace AspnetRun.Core.Repositories 8 | { 9 | public interface IProductRepository : IRepository 10 | { 11 | Task> GetProductListAsync(); 12 | Task> SearchProductsAsync(PageSearchArgs args); 13 | Task> GetProductByNameAsync(string productName); 14 | Task GetProductByIdWithCategoryAsync(int productId); 15 | Task> GetProductByCategoryAsync(int categoryId); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Specifications/Base/BaseSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace AspnetRun.Core.Specifications.Base 6 | { 7 | // Specification Pattern from : https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-implemenation-entity-framework-core 8 | public abstract class BaseSpecification : ISpecification 9 | { 10 | protected BaseSpecification(Expression> criteria) 11 | { 12 | Criteria = criteria; 13 | } 14 | public Expression> Criteria { get; } 15 | public List>> Includes { get; } = new List>>(); 16 | public List IncludeStrings { get; } = new List(); 17 | public Expression> OrderBy { get; private set; } 18 | public Expression> OrderByDescending { get; private set; } 19 | 20 | public int Take { get; private set; } 21 | public int Skip { get; private set; } 22 | public bool isPagingEnabled { get; private set; } = false; 23 | 24 | protected virtual void AddInclude(Expression> includeExpression) 25 | { 26 | Includes.Add(includeExpression); 27 | } 28 | protected virtual void AddInclude(string includeString) 29 | { 30 | IncludeStrings.Add(includeString); 31 | } 32 | protected virtual void ApplyPaging(int skip, int take) 33 | { 34 | Skip = skip; 35 | Take = take; 36 | isPagingEnabled = true; 37 | } 38 | protected virtual void ApplyOrderBy(Expression> orderByExpression) 39 | { 40 | OrderBy = orderByExpression; 41 | } 42 | protected virtual void ApplyOrderByDescending(Expression> orderByDescendingExpression) 43 | { 44 | OrderByDescending = orderByDescendingExpression; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Specifications/Base/ISpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace AspnetRun.Core.Specifications.Base 6 | { 7 | public interface ISpecification 8 | { 9 | Expression> Criteria { get; } 10 | 11 | List>> Includes { get; } 12 | 13 | List IncludeStrings { get; } 14 | 15 | Expression> OrderBy { get; } 16 | 17 | Expression> OrderByDescending { get; } 18 | 19 | // TODO: Paging should be optimized 20 | int Take { get; } 21 | 22 | int Skip { get; } 23 | 24 | bool isPagingEnabled { get; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AspnetRun.Core/Specifications/ProductWithCategorySpecification.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities; 2 | using AspnetRun.Core.Specifications.Base; 3 | 4 | namespace AspnetRun.Core.Specifications 5 | { 6 | public class ProductWithCategorySpecification : BaseSpecification 7 | { 8 | public ProductWithCategorySpecification(string productName) 9 | : base(p => p.Name.Contains(productName)) 10 | { 11 | AddInclude(p => p.Category); 12 | } 13 | 14 | public ProductWithCategorySpecification(int productId) 15 | : base(p => p.Id == productId) 16 | { 17 | AddInclude(p => p.Category); 18 | } 19 | 20 | public ProductWithCategorySpecification() 21 | : base(null) 22 | { 23 | AddInclude(p => p.Category); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/AspnetRun.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.2 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Behaviors/TransactionBehaviour.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Infrastructure.Data; 2 | using MediatR; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace AspnetRun.Infrastructure.Behaviors 10 | { 11 | public class TransactionBehaviour : IPipelineBehavior 12 | { 13 | private readonly ILogger> _logger; 14 | private readonly AspnetRunContext _dbContext; 15 | 16 | public TransactionBehaviour(AspnetRunContext dbContext, 17 | ILogger> logger) 18 | { 19 | _dbContext = dbContext ?? throw new ArgumentException(nameof(AspnetRunContext)); 20 | _logger = logger ?? throw new ArgumentException(nameof(ILogger)); 21 | } 22 | 23 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 24 | { 25 | TResponse response = default; 26 | 27 | try 28 | { 29 | var strategy = _dbContext.Database.CreateExecutionStrategy(); 30 | await strategy.ExecuteAsync(async () => 31 | { 32 | _logger.LogInformation($"Begin transaction {typeof(TRequest).Name}"); 33 | 34 | await _dbContext.BeginTransactionAsync(); 35 | 36 | response = await next(); 37 | 38 | await _dbContext.CommitTransactionAsync(); 39 | 40 | _logger.LogInformation($"Committed transaction {typeof(TRequest).Name}"); 41 | }); 42 | 43 | return response; 44 | } 45 | catch (Exception) 46 | { 47 | _logger.LogInformation($"Rollback transaction executed {typeof(TRequest).Name}"); 48 | 49 | _dbContext.RollbackTransaction(); 50 | throw; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Data/AspnetRunCustomModelBuilder.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace AspnetRun.Infrastructure.Data 5 | { 6 | class AspnetRunCustomModelBuilder : ICustomModelBuilder 7 | { 8 | public void Build(ModelBuilder modelBuilder) 9 | { 10 | modelBuilder.Entity() 11 | .HasKey(psa => new { psa.ProductId, psa.SpecificationId }); 12 | 13 | modelBuilder.Entity() 14 | .HasOne(psa => psa.Product) 15 | .WithMany(p => p.Specifications) 16 | .HasForeignKey(psa => psa.ProductId); 17 | 18 | 19 | modelBuilder.Entity() 20 | .HasKey(psa => new { psa.OrderId, psa.PaymentId }); 21 | 22 | modelBuilder.Entity() 23 | .HasOne(opa => opa.Order) 24 | .WithMany(o => o.Payments) 25 | .HasForeignKey(opa => opa.OrderId); 26 | 27 | 28 | modelBuilder.Entity() 29 | .HasKey(psa => new { psa.ContractId, psa.PaymentId }); 30 | 31 | modelBuilder.Entity() 32 | .HasOne(cpa => cpa.Contract) 33 | .WithMany(c => c.Payments) 34 | .HasForeignKey(cpa => cpa.ContractId); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Data/ICustomModelBuilder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace AspnetRun.Infrastructure.Data 4 | { 5 | public interface ICustomModelBuilder 6 | { 7 | void Build(ModelBuilder modelBuilder); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Exceptions/InfrastructureException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspnetRun.Infrastructure.Exceptions 4 | { 5 | public class InfrastructureException : Exception 6 | { 7 | internal InfrastructureException() 8 | { 9 | } 10 | 11 | internal InfrastructureException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | internal InfrastructureException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/IoC/DependencyRegistrar.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Interfaces; 2 | using AspnetRun.Core.Repositories; 3 | using AspnetRun.Core.Repositories.Base; 4 | using AspnetRun.Infrastructure.Behaviors; 5 | using AspnetRun.Infrastructure.Data; 6 | using AspnetRun.Infrastructure.Logging; 7 | using AspnetRun.Infrastructure.Misc; 8 | using AspnetRun.Infrastructure.Repository; 9 | using AspnetRun.Infrastructure.Repository.Base; 10 | using Autofac; 11 | using MediatR; 12 | using System.Reflection; 13 | 14 | namespace AspnetRun.Infrastructure.IoC 15 | { 16 | public class DependencyRegistrar : IDependencyRegistrar 17 | { 18 | public void Register(ContainerBuilder builder, ITypeFinder typeFinder) 19 | { 20 | // repositories 21 | builder.RegisterType().As().InstancePerDependency(); 22 | builder.RegisterType().As().InstancePerDependency(); 23 | builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerDependency(); 24 | builder.RegisterGeneric(typeof(EnumRepository<>)).As(typeof(IEnumRepository<>)).InstancePerDependency(); 25 | builder.RegisterGeneric(typeof(RepositoryBase<,>)).As(typeof(IRepositoryBase<,>)).InstancePerDependency(); 26 | 27 | builder.RegisterGeneric(typeof(LoggerAdapter<>)).As(typeof(IAppLogger<>)).InstancePerDependency(); 28 | 29 | builder.RegisterType(); 30 | 31 | builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly) 32 | .AsImplementedInterfaces(); 33 | 34 | // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands 35 | //var handlerAssemblies = typeFinder.FindClassesOfType(typeof(IRequestHandler<,>)) 36 | // .Select(t => t.Assembly).Distinct().ToArray(); 37 | //builder.RegisterAssemblyTypes(handlerAssemblies) 38 | // .AsClosedTypesOf(typeof(IRequestHandler<,>)); 39 | 40 | builder.Register(context => 41 | { 42 | var componentContext = context.Resolve(); 43 | return t => { object o; return componentContext.TryResolve(t, out o) ? o : null; }; 44 | }); 45 | 46 | builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>)); 47 | 48 | //// Register the Command's Validators (Validators based on FluentValidation library) 49 | //var validatorAssemblies = typeFinder.FindClassesOfType(typeof(IValidator<,>)) 50 | // .Select(t => t.Assembly).Distinct().ToArray(); 51 | //builder.RegisterAssemblyTypes(validatorAssemblies). 52 | // Where(t => t.IsClosedTypeOf(typeof(IValidator<>))).AsImplementedInterfaces(); 53 | } 54 | 55 | public int Order => 1; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/IoC/IDependencyRegistrar.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Infrastructure.Misc; 2 | using Autofac; 3 | 4 | namespace AspnetRun.Infrastructure.IoC 5 | { 6 | public interface IDependencyRegistrar 7 | { 8 | void Register(ContainerBuilder builder, ITypeFinder typeFinder); 9 | 10 | int Order { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Logging/LoggerAdapter.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Interfaces; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace AspnetRun.Infrastructure.Logging 5 | { 6 | public class LoggerAdapter : IAppLogger 7 | { 8 | private readonly ILogger _logger; 9 | 10 | public LoggerAdapter(ILoggerFactory loggerFactory) 11 | { 12 | _logger = loggerFactory.CreateLogger(); 13 | } 14 | 15 | public void LogWarning(string message, params object[] args) 16 | { 17 | _logger.LogWarning(message, args); 18 | } 19 | 20 | public void LogInformation(string message, params object[] args) 21 | { 22 | _logger.LogInformation(message, args); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Misc/ITypeFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace AspnetRun.Infrastructure.Misc 6 | { 7 | /// 8 | /// Classes implementing this interface provide information about types 9 | /// to various services. 10 | /// 11 | public interface ITypeFinder 12 | { 13 | /// 14 | /// Find classes of type 15 | /// 16 | /// Type 17 | /// A value indicating whether to find only concrete classes 18 | /// Result 19 | IEnumerable FindClassesOfType(bool onlyConcreteClasses = true); 20 | 21 | /// 22 | /// Find classes of type 23 | /// 24 | /// Assign type from 25 | /// A value indicating whether to find only concrete classes 26 | /// Result 27 | /// 28 | IEnumerable FindClassesOfType(Type assignTypeFrom, bool onlyConcreteClasses = true); 29 | 30 | /// 31 | /// Find classes of type 32 | /// 33 | /// Type 34 | /// Assemblies 35 | /// A value indicating whether to find only concrete classes 36 | /// Result 37 | IEnumerable FindClassesOfType(IEnumerable assemblies, bool onlyConcreteClasses = true); 38 | 39 | /// 40 | /// Find classes of type 41 | /// 42 | /// Assign type from 43 | /// Assemblies 44 | /// A value indicating whether to find only concrete classes 45 | /// Result 46 | IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses = true); 47 | 48 | /// 49 | /// Gets the assemblies related to the current implementation. 50 | /// 51 | /// A list of assemblies 52 | IList GetAssemblies(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Misc/WebAppTypeFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace AspnetRun.Infrastructure.Misc 6 | { 7 | /// 8 | /// Provides information about types in the current web application. 9 | /// Optionally this class can look at all assemblies in the bin folder. 10 | /// 11 | public class WebAppTypeFinder : AppDomainTypeFinder 12 | { 13 | #region Fields 14 | 15 | private bool _binFolderAssembliesLoaded; 16 | 17 | #endregion 18 | 19 | #region Ctor 20 | 21 | public WebAppTypeFinder(IAppFileProvider fileProvider = null) : base(fileProvider) 22 | { 23 | } 24 | 25 | #endregion 26 | 27 | #region Properties 28 | 29 | /// 30 | /// Gets or sets whether assemblies in the bin folder of the web application should be specifically checked for being loaded on application load. This is need in situations where plugins need to be loaded in the AppDomain after the application been reloaded. 31 | /// 32 | public bool EnsureBinFolderAssembliesLoaded { get; set; } = true; 33 | 34 | #endregion 35 | 36 | #region Methods 37 | 38 | /// 39 | /// Gets a physical disk path of \Bin directory 40 | /// 41 | /// The physical path. E.g. "c:\inetpub\wwwroot\bin" 42 | public virtual string GetBinDirectory() 43 | { 44 | return AppContext.BaseDirectory; 45 | } 46 | 47 | /// 48 | /// Get assemblies 49 | /// 50 | /// Result 51 | public override IList GetAssemblies() 52 | { 53 | if (!EnsureBinFolderAssembliesLoaded || _binFolderAssembliesLoaded) 54 | return base.GetAssemblies(); 55 | 56 | _binFolderAssembliesLoaded = true; 57 | var binPath = GetBinDirectory(); 58 | //binPath = _webHelper.MapPath("~/bin"); 59 | LoadMatchingAssemblies(binPath); 60 | 61 | return base.GetAssemblies(); 62 | } 63 | 64 | #endregion 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Paging/PagedList.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Paging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | 7 | namespace AspnetRun.Infrastructure.Paging 8 | { 9 | public class PagedList : IPagedList 10 | { 11 | public PagedList(int pageIndex, int pageSize, int totalCount, int totalPages, IEnumerable items) 12 | { 13 | PageIndex = pageIndex; 14 | PageSize = pageSize; 15 | TotalCount = totalCount; 16 | TotalPages = totalPages; 17 | Items = items; 18 | } 19 | 20 | public PagedList(IQueryable query, PagingArgs pagingArgs, 21 | List>>> orderByList = null, 22 | List>>> filterList = null) 23 | { 24 | query = query.OrderBy(orderByList); 25 | query = query.Where(filterList); 26 | 27 | PageIndex = pagingArgs.PageIndex < 1 ? 1 : pagingArgs.PageIndex; 28 | PageSize = pagingArgs.PageSize < 1 ? 10 : pagingArgs.PageSize; 29 | 30 | TotalCount = 0; 31 | 32 | var items = pagingArgs.PagingStrategy == PagingStrategy.NoCount 33 | ? query.Skip((PageIndex - 1) * PageSize).Take(PageSize + 1).ToList() 34 | : ( 35 | (TotalCount = query.Count()) > 0 36 | ? query.Skip((PageIndex - 1) * PageSize).Take(PageSize).ToList() 37 | : new List() 38 | ); 39 | 40 | TotalCount = pagingArgs.PagingStrategy == PagingStrategy.NoCount 41 | ? (PageIndex - 1) * PageSize + items.Count 42 | : TotalCount; 43 | 44 | TotalPages = TotalCount / PageSize; 45 | 46 | if (TotalCount % PageSize > 0) 47 | TotalPages++; 48 | 49 | if (pagingArgs.PagingStrategy == PagingStrategy.NoCount && items.Count == PageSize + 1) 50 | { 51 | items.RemoveAt(PageSize); 52 | } 53 | 54 | Items = items; 55 | } 56 | 57 | /// 58 | /// Page index 59 | /// 60 | public int PageIndex { get; } 61 | 62 | /// 63 | /// Page size 64 | /// 65 | public int PageSize { get; } 66 | 67 | /// 68 | /// Total count 69 | /// 70 | public int TotalCount { get; } 71 | 72 | /// 73 | /// Total pages 74 | /// 75 | public int TotalPages { get; } 76 | 77 | /// 78 | /// Has previous page 79 | /// 80 | public bool HasPreviousPage => PageIndex > 0; 81 | 82 | /// 83 | /// Has next page 84 | /// 85 | public bool HasNextPage => PageIndex + 1 < TotalPages; 86 | 87 | /// 88 | /// Items 89 | /// 90 | public IEnumerable Items { get; } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Paging/PagingExtensions.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Paging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | 7 | namespace AspnetRun.Infrastructure.Paging 8 | { 9 | public static class PagingExtensions 10 | { 11 | public static IQueryable OrderBy(this IQueryable query, List>>> orderByList) 12 | { 13 | if (orderByList == null) 14 | return query; 15 | 16 | orderByList = orderByList.OrderBy(ob => ob.Item1.Priority).ToList(); 17 | 18 | IOrderedQueryable orderedQuery = null; 19 | foreach (var orderBy in orderByList) 20 | { 21 | if (orderedQuery == null) 22 | { 23 | orderedQuery = orderBy.Item1.Direction == SortingOption.SortingDirection.ASC ? query.OrderBy(orderBy.Item2) : query.OrderByDescending(orderBy.Item2); 24 | } 25 | else 26 | { 27 | orderedQuery = orderBy.Item1.Direction == SortingOption.SortingDirection.ASC ? orderedQuery.ThenBy(orderBy.Item2) : orderedQuery.ThenByDescending(orderBy.Item2); 28 | } 29 | } 30 | 31 | return orderedQuery ?? query; 32 | } 33 | 34 | public static IQueryable Where(this IQueryable query, List>>> filterList) 35 | { 36 | if (filterList == null) 37 | return query; 38 | 39 | foreach (var filter in filterList) 40 | { 41 | query = query.Where(filter.Item2); 42 | } 43 | 44 | return query; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Repository/Base/EnumRepository.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using AspnetRun.Core.Repositories.Base; 3 | using AspnetRun.Infrastructure.Data; 4 | 5 | namespace AspnetRun.Infrastructure.Repository.Base 6 | { 7 | public class EnumRepository : RepositoryBase, IEnumRepository 8 | where T : class, IEntityBase 9 | { 10 | public EnumRepository(AspnetRunContext context) 11 | : base(context) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Repository/Base/Repository.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using AspnetRun.Core.Repositories.Base; 3 | using AspnetRun.Infrastructure.Data; 4 | 5 | namespace AspnetRun.Infrastructure.Repository.Base 6 | { 7 | public class Repository : RepositoryBase, IRepository 8 | where T : class, IEntityBase 9 | { 10 | public Repository(AspnetRunContext context) 11 | : base(context) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Repository/Base/SpecificationEvaluator.cs: -------------------------------------------------------------------------------- 1 | using AspnetRun.Core.Entities.Base; 2 | using AspnetRun.Core.Specifications.Base; 3 | using Microsoft.EntityFrameworkCore; 4 | using System.Linq; 5 | 6 | namespace AspnetRun.Infrastructure.Repository.Base 7 | { 8 | public class SpecificationEvaluator where T : class, IEntityBase 9 | { 10 | public static IQueryable GetQuery(IQueryable inputQuery, ISpecification specification) 11 | { 12 | var query = inputQuery; 13 | 14 | // modify the IQueryable using the specification's criteria expression 15 | if (specification.Criteria != null) 16 | { 17 | query = query.Where(specification.Criteria); 18 | } 19 | 20 | // Includes all expression-based includes 21 | query = specification.Includes.Aggregate(query, (current, include) => current.Include(include)); 22 | 23 | // Include any string-based include statements 24 | query = specification.IncludeStrings.Aggregate(query, (current, include) => current.Include(include)); 25 | 26 | // Apply ordering if expressions are set 27 | if (specification.OrderBy != null) 28 | { 29 | query = query.OrderBy(specification.OrderBy); 30 | } 31 | else if (specification.OrderByDescending != null) 32 | { 33 | query = query.OrderByDescending(specification.OrderByDescending); 34 | } 35 | 36 | // Apply paging if enabled 37 | if (specification.isPagingEnabled) 38 | { 39 | query = query.Skip(specification.Skip) 40 | .Take(specification.Take); 41 | } 42 | return query; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/AspnetRun.Infrastructure/Repository/CategoryRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Threading.Tasks; 5 | using AspnetRun.Core.Entities; 6 | using AspnetRun.Core.Paging; 7 | using AspnetRun.Core.Repositories; 8 | using AspnetRun.Infrastructure.Data; 9 | using AspnetRun.Infrastructure.Paging; 10 | using AspnetRun.Infrastructure.Repository.Base; 11 | 12 | namespace AspnetRun.Infrastructure.Repository 13 | { 14 | public class CategoryRepository : Repository, ICategoryRepository 15 | { 16 | public CategoryRepository(AspnetRunContext dbContext) 17 | : base(dbContext) 18 | { 19 | } 20 | 21 | public Task> SearchCategoriesAsync(PageSearchArgs args) 22 | { 23 | var query = Table; 24 | 25 | var orderByList = new List>>>(); 26 | 27 | if (args.SortingOptions != null) 28 | { 29 | foreach (var sortingOption in args.SortingOptions) 30 | { 31 | switch (sortingOption.Field) 32 | { 33 | case "id": 34 | orderByList.Add(new Tuple>>(sortingOption, c => c.Id)); 35 | break; 36 | case "name": 37 | orderByList.Add(new Tuple>>(sortingOption, c => c.Name)); 38 | break; 39 | case "description": 40 | orderByList.Add(new Tuple>>(sortingOption, c => c.Description)); 41 | break; 42 | } 43 | } 44 | } 45 | 46 | if (orderByList.Count == 0) 47 | { 48 | orderByList.Add(new Tuple>>(new SortingOption { Direction = SortingOption.SortingDirection.ASC }, c => c.Id)); 49 | } 50 | 51 | var filterList = new List>>>(); 52 | 53 | if (args.FilteringOptions != null) 54 | { 55 | foreach (var filteringOption in args.FilteringOptions) 56 | { 57 | switch (filteringOption.Field) 58 | { 59 | case "id": 60 | filterList.Add(new Tuple>>(filteringOption, c => c.Id == (int)filteringOption.Value)); 61 | break; 62 | case "name": 63 | filterList.Add(new Tuple>>(filteringOption, c => c.Name.Contains((string)filteringOption.Value))); 64 | break; 65 | case "description": 66 | filterList.Add(new Tuple>>(filteringOption, c => c.Description.Contains((string)filteringOption.Value))); 67 | break; 68 | } 69 | } 70 | } 71 | 72 | var categoryPagedList = new PagedList(query, new PagingArgs { PageIndex = args.PageIndex, PageSize = args.PageSize, PagingStrategy = args.PagingStrategy }, orderByList, filterList); 73 | 74 | return Task.FromResult>(categoryPagedList); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/README.md: -------------------------------------------------------------------------------- 1 | # AspnetRun 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.3.8. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/AspnetRun.Web/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /src/AspnetRun.Web/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('Welcome to AspnetRun!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root h1')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aspnet-run-angular", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve -o", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~8.2.7", 15 | "@angular/common": "~8.2.7", 16 | "@angular/compiler": "~8.2.7", 17 | "@angular/core": "~8.2.7", 18 | "@angular/forms": "~8.2.7", 19 | "@angular/platform-browser": "~8.2.7", 20 | "@angular/platform-browser-dynamic": "~8.2.7", 21 | "@angular/router": "~8.2.7", 22 | "@coreui/angular": "^2.5.3", 23 | "@coreui/coreui": "^2.1.12", 24 | "@coreui/icons": "^0.3.0", 25 | "@ng-select/ng-select": "^2.20.5", 26 | "@ngx-translate/core": "^11.0.1", 27 | "@ngx-translate/http-loader": "^4.0.0", 28 | "angular-slickgrid": "^2.10.5", 29 | "bootstrap": "^4.3.1", 30 | "core-js": "^2.6.9", 31 | "font-awesome": "^4.7.0", 32 | "jquery": "^3.4.1", 33 | "lodash": "^4.17.15", 34 | "moment": "^2.24.0", 35 | "ng-wizard": "^1.0.0", 36 | "ngx-bootstrap": "^4.3.0", 37 | "ngx-perfect-scrollbar": "^7.2.1", 38 | "ngx-toastr": "^11.1.0", 39 | "ngx-ui-loader": "^7.2.2", 40 | "rxjs": "^6.5.3", 41 | "simple-line-icons": "^2.4.1", 42 | "tslib": "^1.10.0", 43 | "zone.js": "~0.9.1" 44 | }, 45 | "devDependencies": { 46 | "@angular-devkit/build-angular": "~0.803.5", 47 | "@angular/cli": "~8.3.5", 48 | "@angular/compiler-cli": "~8.2.7", 49 | "@angular/language-service": "~8.2.7", 50 | "@types/jasmine": "~2.8.8", 51 | "@types/jasminewd2": "~2.0.3", 52 | "@types/node": "~8.9.4", 53 | "codelyzer": "^5.0.1", 54 | "jasmine-core": "~2.99.1", 55 | "jasmine-spec-reporter": "~4.2.1", 56 | "karma": "~4.0.0", 57 | "karma-chrome-launcher": "~2.2.0", 58 | "karma-coverage-istanbul-reporter": "^2.0.6", 59 | "karma-jasmine": "~1.1.2", 60 | "karma-jasmine-html-reporter": "^0.2.2", 61 | "protractor": "~5.4.0", 62 | "ts-node": "~7.0.0", 63 | "tslint": "~5.11.0", 64 | "typescript": "~3.5.3" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/_nav.ts: -------------------------------------------------------------------------------- 1 | interface NavAttributes { 2 | [propName: string]: any; 3 | } 4 | interface NavWrapper { 5 | attributes: NavAttributes; 6 | element: string; 7 | } 8 | interface NavBadge { 9 | text: string; 10 | variant: string; 11 | } 12 | interface NavLabel { 13 | class?: string; 14 | variant: string; 15 | } 16 | 17 | export interface NavData { 18 | name?: string; 19 | url?: string; 20 | icon?: string; 21 | badge?: NavBadge; 22 | title?: boolean; 23 | children?: NavData[]; 24 | variant?: string; 25 | attributes?: NavAttributes; 26 | divider?: boolean; 27 | class?: string; 28 | label?: NavLabel; 29 | wrapper?: NavWrapper; 30 | } 31 | 32 | export const navItems: NavData[] = [ 33 | { name: 'Dashboard', url: '/dashboard', icon: 'icon-speedometer', }, 34 | { name: 'Products', url: '/product/product-list', icon: 'icon-list' }, 35 | { name: 'Categories', url: '/category/category-list', icon: 'icon-folder-alt' }, 36 | { name: 'Customer', url: '/customer/customer-list', icon: 'icon-folder-alt' }, 37 | { name: 'About', url: '/about', icon: 'icon-folder-alt' }, 38 | ]; 39 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule, PreloadAllModules } from '@angular/router'; 3 | import { AuthGuardService } from './core/services/auth-guard.service'; 4 | import { P404Component } from './views/error/404.component'; 5 | import { P500Component } from './views/error/500.component'; 6 | import { LoginComponent } from './views/login/login.component'; 7 | import { RegisterComponent } from './views/register/register.component'; 8 | import { LayoutComponent } from './core/layout/layout.component'; 9 | import { DashboardComponent } from './views/dashboard/dashboard.component'; 10 | import { AboutComponent } from './views/about/about.component'; 11 | 12 | export const routes: Routes = [ 13 | { path: '', redirectTo: 'dashboard', pathMatch: 'full', }, 14 | { path: '404', component: P404Component, data: { title: 'Page 404' } }, 15 | { path: '500', component: P500Component, data: { title: 'Page 500' } }, 16 | { path: 'login', component: LoginComponent, data: { title: 'Login Page' } }, 17 | { path: 'register', component: RegisterComponent, data: { title: 'Register Page' } }, 18 | { 19 | path: '', component: LayoutComponent, data: { title: '' }, 20 | children: [ 21 | { path: 'dashboard', component: DashboardComponent, data: { title: 'Dashboard' } }, 22 | { path: 'product', loadChildren: () => import('./views/product/product.module').then(m => m.ProductModule) }, 23 | { path: 'category', loadChildren: () => import('./views/category/category.module').then(m => m.CategoryModule) }, 24 | { path: 'customer', loadChildren: () => import('./views/customer/customer.module').then(m => m.CustomerModule) }, 25 | { path: 'about', component: AboutComponent, data: { title: 'About' } } 26 | ], 27 | canActivateChild: [AuthGuardService], 28 | canActivate: [AuthGuardService] 29 | }, 30 | { path: '**', component: P404Component } 31 | ]; 32 | 33 | @NgModule({ 34 | imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules, enableTracing: false })], 35 | exports: [RouterModule] 36 | }) 37 | export class AppRoutingModule { } 38 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/app.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router, NavigationEnd } from '@angular/router'; 3 | import { SpinnerService } from './core/services/spinner.service'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: './app.component.html', 8 | styleUrls: ['./app.component.css'] 9 | }) 10 | export class AppComponent implements OnInit { 11 | constructor(private router: Router, public spinnerService: SpinnerService) { } 12 | 13 | ngOnInit() { 14 | this.router.events.subscribe((evt) => { 15 | if (!(evt instanceof NavigationEnd)) { 16 | return; 17 | } 18 | window.scrollTo(0, 0); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 3 | import { NgModule } from '@angular/core'; 4 | 5 | import { AppComponent } from './app.component'; 6 | import { AppRoutingModule } from './app-routing.module'; 7 | 8 | import { CoreModule } from './core/core.module'; 9 | import { SharedModule } from './shared/shared.module'; 10 | 11 | import { P404Component } from './views/error/404.component'; 12 | import { P500Component } from './views/error/500.component'; 13 | import { LoginComponent } from './views/login/login.component'; 14 | import { RegisterComponent } from './views/register/register.component'; 15 | import { DashboardComponent } from './views/dashboard/dashboard.component'; 16 | import { AboutComponent } from './views/about/about.component'; 17 | 18 | @NgModule({ 19 | declarations: [ 20 | AppComponent, 21 | P404Component, 22 | P500Component, 23 | LoginComponent, 24 | RegisterComponent, 25 | DashboardComponent, 26 | AboutComponent, 27 | ], 28 | imports: [ 29 | BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), 30 | BrowserAnimationsModule, 31 | AppRoutingModule, // Main routes for application 32 | CoreModule, // Singleton objects (services, components that are loaded only once, etc.) 33 | SharedModule, // Shared (multi-instance) objects 34 | ], 35 | providers: [], 36 | bootstrap: [AppComponent] 37 | }) 38 | export class AppModule { } 39 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/core.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, Optional, SkipSelf } from '@angular/core'; 2 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; 3 | 4 | import { ProductDataService } from './services/product-data.service'; 5 | import { CategoryDataService } from './services/category-data.service'; 6 | import { ValidationService } from './services/validation.service'; 7 | import { EnsureModuleLoadedOnceGuard } from './ensure-module-loaded-once.guard'; 8 | import { HttpErrorInterceptor } from './interceptors/http-error.interceptor'; 9 | import { HttpAuthInterceptor } from './interceptors/http-auth.interceptor'; 10 | import { NgxUiLoaderModule, NgxUiLoaderHttpModule } from 'ngx-ui-loader'; 11 | import { SpinnerService } from './services/spinner.service'; 12 | import { AuthService } from './services/auth.service'; 13 | import { AuthGuardService } from './services/auth-guard.service'; 14 | import { LayoutComponent } from './layout/layout.component'; 15 | import { AppAsideModule, AppBreadcrumbModule, AppHeaderModule, AppFooterModule, AppSidebarModule } from '@coreui/angular'; 16 | import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; 17 | import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; 18 | import { TabsModule } from 'ngx-bootstrap/tabs'; 19 | import { RouterModule } from '@angular/router'; 20 | import { CustomerDataService } from './services/customer-data.services'; 21 | import { PageService } from './services/page.service'; 22 | import { ToastrModule } from 'ngx-toastr'; 23 | import { NgWizardModule, NgWizardConfig, THEME } from 'ng-wizard'; 24 | 25 | const APP_CONTAINERS = [LayoutComponent]; 26 | 27 | const ngWizardConfig: NgWizardConfig = { 28 | theme: THEME.default 29 | }; 30 | 31 | @NgModule({ 32 | declarations: [ 33 | ...APP_CONTAINERS, 34 | ], 35 | imports: [ 36 | RouterModule, 37 | NgxUiLoaderModule, 38 | //NgxUiLoaderRouterModule, // import this module for showing loader automatically when navigating between app routes 39 | NgxUiLoaderHttpModule.forRoot({ showForeground: false }), 40 | ToastrModule.forRoot({ positionClass: 'toast-top-full-width', closeButton: true }), 41 | AppAsideModule, 42 | AppBreadcrumbModule.forRoot(), 43 | AppFooterModule, 44 | AppHeaderModule, 45 | AppSidebarModule, 46 | PerfectScrollbarModule, 47 | BsDropdownModule.forRoot(), 48 | TabsModule.forRoot(), 49 | NgWizardModule.forRoot(ngWizardConfig), 50 | ], 51 | exports: [ 52 | RouterModule, 53 | HttpClientModule, 54 | NgxUiLoaderModule, 55 | NgWizardModule, 56 | LayoutComponent, 57 | ], 58 | providers: [ 59 | ProductDataService, 60 | CategoryDataService, 61 | CustomerDataService, 62 | AuthService, 63 | AuthGuardService, 64 | ValidationService, 65 | PageService, 66 | SpinnerService, 67 | { 68 | provide: HTTP_INTERCEPTORS, 69 | useClass: HttpErrorInterceptor, 70 | multi: true 71 | }, 72 | { 73 | provide: HTTP_INTERCEPTORS, 74 | useClass: HttpAuthInterceptor, 75 | multi: true 76 | }, 77 | ] // these should be singleton 78 | }) 79 | export class CoreModule extends EnsureModuleLoadedOnceGuard { // Ensure that CoreModule is only loaded into AppModule 80 | 81 | // Looks for the module in the parent injector to see if it's already been loaded (only want it loaded once) 82 | constructor(@Optional() @SkipSelf() parentModule: CoreModule) { 83 | super(parentModule); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/ensure-module-loaded-once.guard.ts: -------------------------------------------------------------------------------- 1 | export class EnsureModuleLoadedOnceGuard { 2 | constructor(targetModule: any) { 3 | if (targetModule) { 4 | throw new Error(`${targetModule.constructor.name} has already been loaded. Import this module in the AppModule only.`); 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/interceptors/http-auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | 6 | @Injectable() 7 | export class HttpAuthInterceptor implements HttpInterceptor { 8 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 9 | const authToken = localStorage.getItem("authToken"); 10 | 11 | if (authToken) { 12 | const cloned = request.clone({ 13 | headers: request.headers.set("Authorization", "Bearer " + authToken) 14 | }); 15 | 16 | return next.handle(cloned); 17 | } 18 | else { 19 | return next.handle(request); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/interceptors/http-error.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http'; 2 | import { Observable, empty } from 'rxjs'; 3 | import { catchError } from 'rxjs/operators'; 4 | import { environment } from 'src/environments/environment'; 5 | import { Injectable } from '@angular/core'; 6 | import { ToastrService } from 'ngx-toastr'; 7 | 8 | @Injectable() 9 | export class HttpErrorInterceptor implements HttpInterceptor { 10 | constructor(private toastr: ToastrService) { 11 | } 12 | 13 | intercept(request: HttpRequest, next: HttpHandler): Observable> { 14 | return next.handle(request) 15 | .pipe( 16 | catchError((error: HttpErrorResponse) => { 17 | let errorMessage = ''; 18 | 19 | if (error.error instanceof ErrorEvent) { 20 | // client-side error 21 | errorMessage = `Error: ${error.error.message}`; 22 | } 23 | else { 24 | // server-side error 25 | errorMessage = `Error Code: ${error.status} | Message: ${error.message}`; 26 | } 27 | 28 | if (!environment.production) { 29 | console.log(error); 30 | } 31 | 32 | this.toastr.error(errorMessage); 33 | console.log(errorMessage); 34 | 35 | return empty(); 36 | }) 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/layout/layout.component.html: -------------------------------------------------------------------------------- 1 | 5 | 17 | 18 |
19 | 20 | 21 | 22 | 23 |
24 | 25 | 29 | 30 |
31 | 32 |
33 |
34 | 35 | 36 | 37 | 38 |
39 |
40 |
41 | 42 | 43 |
44 |
45 |
46 | 47 | 48 |
49 |
50 |
51 |
52 |
53 |
54 | 55 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/layout/layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnDestroy, Inject } from '@angular/core'; 2 | import { DOCUMENT } from '@angular/common'; 3 | import { navItems } from '../../_nav'; 4 | import { Router } from '@angular/router'; 5 | import { AuthService } from '../services/auth.service'; 6 | 7 | 8 | @Component({ 9 | selector: 'app-layout', 10 | templateUrl: './layout.component.html' 11 | }) 12 | export class LayoutComponent implements OnDestroy { 13 | public navItems = navItems; 14 | public sidebarMinimized = true; 15 | private changes: MutationObserver; 16 | public element: HTMLElement; 17 | 18 | constructor( 19 | private router: Router, 20 | private authService: AuthService, 21 | @Inject(DOCUMENT) _document?: any) { 22 | 23 | this.changes = new MutationObserver((mutations) => { 24 | this.sidebarMinimized = _document.body.classList.contains('sidebar-minimized'); 25 | }); 26 | this.element = _document.body; 27 | this.changes.observe(this.element, { 28 | attributes: true, 29 | attributeFilter: ['class'] 30 | }); 31 | } 32 | 33 | ngOnDestroy(): void { 34 | this.changes.disconnect(); 35 | } 36 | 37 | logout() { 38 | this.authService.logout(); 39 | this.router.navigateByUrl('/login'); 40 | } 41 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CanActivate, Router, CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router'; 3 | import { AuthService } from './auth.service'; 4 | import { Observable } from 'rxjs'; 5 | 6 | @Injectable() 7 | export class AuthGuardService implements CanActivate, CanActivateChild { 8 | constructor(private authService: AuthService, private router: Router) { } 9 | 10 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable | Promise { 11 | var isLoggedIn = this.authService.isLoggedIn(); 12 | 13 | if (!isLoggedIn) { 14 | if (state.url == "/dashboard") { 15 | this.router.navigate(['login']); 16 | } else { 17 | this.router.navigate(['login'], { queryParams: { returnUrl: state.url } }); 18 | } 19 | } 20 | 21 | return isLoggedIn; 22 | } 23 | 24 | canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable | Promise { 25 | return this.canActivate(childRoute, state); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import * as moment from "moment"; 4 | import { environment } from 'src/environments/environment'; 5 | import { tap } from 'rxjs/operators'; 6 | 7 | @Injectable() 8 | export class AuthService { 9 | 10 | constructor(private httpClient: HttpClient) { } 11 | 12 | login(email: string, password: string) { 13 | var request = { email: email, password: password }; 14 | 15 | return this.httpClient.post(environment.apiUrl + '/Account/CreateToken', request) 16 | .pipe(tap(this.setSession)); 17 | } 18 | 19 | private setSession(authResult: any) { 20 | const authExpiration = moment(authResult.expiration); 21 | 22 | localStorage.setItem('authToken', authResult.token); 23 | localStorage.setItem("authExpiration", JSON.stringify(authExpiration.valueOf())); 24 | } 25 | 26 | logout() { 27 | localStorage.removeItem("authToken"); 28 | localStorage.removeItem("authExpiration"); 29 | } 30 | 31 | public isLoggedIn() { 32 | return moment().isBefore(this.getExpiration()); 33 | } 34 | 35 | isLoggedOut() { 36 | return !this.isLoggedIn(); 37 | } 38 | 39 | getExpiration() { 40 | const expiration = localStorage.getItem("authExpiration"); 41 | const expiresAt = JSON.parse(expiration); 42 | return moment(expiresAt); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/category-data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { environment } from 'src/environments/environment'; 5 | import { ICategory, IPagedList } from 'src/app/shared/interfaces'; 6 | 7 | @Injectable() 8 | export class CategoryDataService { 9 | constructor(private httpClient: HttpClient) { } 10 | 11 | getCategories(): Observable { 12 | return this.httpClient.get(environment.apiUrl + '/Category/GetCategories'); 13 | } 14 | 15 | searchCategories(args: any): Observable> { 16 | var request = { args: args }; 17 | 18 | return this.httpClient.post>(environment.apiUrl + '/Category/SearchCategories', request); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/customer-data.services.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { environment } from 'src/environments/environment'; 5 | import { ICustomer } from 'src/app/shared/interfaces'; 6 | import { map } from 'rxjs/operators'; 7 | 8 | @Injectable() 9 | export class CustomerDataService { 10 | 11 | constructor(private httpClient: HttpClient) { } 12 | 13 | getCustomers(): Observable { 14 | return this.httpClient.get(environment.apiUrl + '/Customer/GetCustomers'); 15 | } 16 | 17 | getCustomerById(id:number): Observable { 18 | var request = { id: id }; 19 | 20 | return this.httpClient.post(environment.apiUrl + '/Customer/GetCustomerById/', request); 21 | } 22 | 23 | deleteCustomer(id: number) { 24 | return this.httpClient.delete(environment.apiUrl + '/' + id) 25 | .pipe( 26 | map(res => true) 27 | ); 28 | } 29 | 30 | updateCustomer(id: number): Observable { 31 | return this.httpClient.delete(environment.apiUrl + '/' + id) 32 | .pipe( 33 | map(res => true) 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/logger.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { environment } from '../../../environments/environment'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class LoggerService { 8 | 9 | constructor() { } 10 | 11 | log(msg: string) { 12 | if (!environment.production) { 13 | console.log(msg); 14 | } 15 | else { 16 | // AppInsights 17 | } 18 | 19 | } 20 | 21 | logError(msg: string) { 22 | if (!environment.production) { 23 | console.error(msg); 24 | } 25 | else { 26 | // AppInsights 27 | } 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/page.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { AngularGridInstance } from 'angular-slickgrid'; 3 | 4 | @Injectable() 5 | export class PageService { 6 | getPageArgs(angularGrid: AngularGridInstance): any { 7 | if (angularGrid) { 8 | var filteringOptions = angularGrid.backendService.options.filteringOptions; 9 | var sortingOptions = angularGrid.backendService.options.sortingOptions; 10 | var paginationOptions = angularGrid.backendService.getCurrentPagination(); 11 | 12 | return { 13 | pageIndex: paginationOptions ? paginationOptions.pageNumber : 1, 14 | pageSize: paginationOptions ? paginationOptions.pageSize : 10, 15 | filteringOptions: filteringOptions, 16 | sortingOptions: sortingOptions 17 | }; 18 | } 19 | else { 20 | return { 21 | pageIndex: 1, 22 | pageSize: 10 23 | }; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/product-data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { environment } from 'src/environments/environment'; 5 | import { IProduct, IPagedList } from 'src/app/shared/interfaces'; 6 | 7 | @Injectable() 8 | export class ProductDataService { 9 | constructor(private httpClient: HttpClient) { } 10 | 11 | getProducts(): Observable { 12 | return this.httpClient.get(environment.apiUrl + '/Product/GetProducts'); 13 | } 14 | 15 | searchProducts(args: any): Observable> { 16 | var request = { args: args }; 17 | 18 | return this.httpClient.post>(environment.apiUrl + '/Product/SearchProducts', request); 19 | } 20 | 21 | getProductById(id: number): Observable { 22 | var request = { id: id }; 23 | 24 | return this.httpClient.post(environment.apiUrl + '/Product/GetProductById', request); 25 | } 26 | 27 | getProductsByName(name: string): Observable { 28 | var request = { name: name }; 29 | 30 | return this.httpClient.post(environment.apiUrl + '/Product/GetProductsByName', request); 31 | } 32 | 33 | getProductsByCategoryId(categoryId: string): Observable { 34 | var request = { categoryId: categoryId }; 35 | 36 | return this.httpClient.post(environment.apiUrl + '/Product/GetProductsByCategoryId', request); 37 | } 38 | 39 | createProduct(product: IProduct): Observable { 40 | var request = { product: product }; 41 | 42 | return this.httpClient.post(environment.apiUrl + '/Product/CreateProduct', request); 43 | } 44 | 45 | updateProduct(product: IProduct): Observable { 46 | var request = { product: product }; 47 | 48 | return this.httpClient.post(environment.apiUrl + '/Product/UpdateProduct', request); 49 | } 50 | 51 | deleteProductById(id: number): Observable { 52 | var request = { id: id }; 53 | 54 | return this.httpClient.post(environment.apiUrl + '/Product/DeleteProductById', request); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/spinner.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { NgxUiLoaderConfig, NgxUiLoaderService } from 'ngx-ui-loader'; 3 | 4 | @Injectable() 5 | export class SpinnerService { 6 | config: NgxUiLoaderConfig; 7 | 8 | constructor(private ngxUiLoaderService: NgxUiLoaderService) { 9 | this.config = this.ngxUiLoaderService.getDefaultConfig(); 10 | this.config.fgsSize = 40; // 60 11 | this.config.fgsType = "rectangle-bounce-pulse-out"; // "ball-spin-clockwise" 12 | this.config.bgsOpacity = 1; // 0.5 13 | //this.config.bgsPosition = "center-center"; // "bottom-right" 14 | this.config.bgsSize = 40; // 60 15 | this.config.bgsType = "rectangle-bounce-pulse-out"; // "ball-spin-clockwise" 16 | this.config.blur = 1; // 5 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/core/services/validation.service.ts: -------------------------------------------------------------------------------- 1 | import { AbstractControl } from '@angular/forms'; 2 | import { Injectable } from '@angular/core'; 3 | 4 | @Injectable() 5 | export class ValidationService { 6 | 7 | getValidatorErrorMessage(name: string, value?: any) { 8 | const config = { 9 | 'required': 'Required', 10 | 'invalidEmailAddress': 'Invalid email address', 11 | 'invalidPassword': 'This regex can be used to restrict passwords to a length of 8 to 20 aplhanumeric characters and select special characters. The password also can not start with a digit, underscore or special character and must contain at least one digit.', 12 | 'minlength': `Minimum length ${value.requiredLength}`, 13 | 'maxlength': `Maximum length ${value.requiredLength}` 14 | }; 15 | return config[name]; 16 | } 17 | 18 | email(control: AbstractControl) { 19 | if (control.value.match(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/)) { 20 | return null; 21 | } else { 22 | return { 'invalidEmailAddress': true }; 23 | } 24 | } 25 | 26 | password(control: AbstractControl) { 27 | if (control.value.match(/^(?=[^\d_].*?\d)\w(\w|[!@#$%]){7,20}/)) { 28 | return null; 29 | } else { 30 | return { 'invalidPassword': true }; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/shared/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface IProduct { 2 | id: number; 3 | name: string; 4 | description: string; 5 | unitPrice: number; 6 | category: ICategory; 7 | } 8 | 9 | export interface ICategory { 10 | id: number; 11 | name?: string; 12 | description?: string; 13 | } 14 | 15 | export interface ICustomer { 16 | id: number; 17 | firstName: string; 18 | lastName: string; 19 | gender: string; 20 | address: string; 21 | city: string; 22 | state: IState; 23 | orders?: IOrder[]; 24 | orderTotal?: number; 25 | latitude?: number; 26 | longitude?: number; 27 | } 28 | 29 | export interface IState { 30 | abbreviation: string; 31 | name: string; 32 | } 33 | 34 | export interface IOrder { 35 | productName: string; 36 | itemCost: number; 37 | } 38 | 39 | export interface IOrderItem { 40 | id: number; 41 | productName: string; 42 | itemCost: number; 43 | } 44 | 45 | export interface IPagedList { 46 | pageIndex: number; 47 | pageSize: number; 48 | totalCount: number; 49 | totalPages: number; 50 | hasPreviousPage: boolean; 51 | hasNextPage: boolean; 52 | items: T[]; 53 | } 54 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/shared/pipes/capitalize.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ name: 'capitalize' }) 4 | export class CapitalizePipe implements PipeTransform { 5 | 6 | transform(value: any) { 7 | return typeof value === 'string' && value.charAt(0).toUpperCase() + value.slice(1) || value; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/shared/pipes/trim.pipe.ts: -------------------------------------------------------------------------------- 1 | import {Pipe, PipeTransform} from '@angular/core'; 2 | 3 | @Pipe({name: 'trim'}) 4 | export class TrimPipe implements PipeTransform { 5 | transform(value: any) { 6 | if (!value) { 7 | return ''; 8 | } 9 | return value.trim(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/shared/shared.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | 5 | import { ModalModule } from 'ngx-bootstrap/modal'; 6 | import { NgSelectModule } from '@ng-select/ng-select'; 7 | import { AngularSlickgridModule } from 'angular-slickgrid'; 8 | 9 | import { ValidationMessageComponent } from './validation-message/validation-message.component'; 10 | import { CapitalizePipe } from './pipes/capitalize.pipe'; 11 | import { TrimPipe } from './pipes/trim.pipe'; 12 | 13 | @NgModule({ 14 | imports: [ 15 | CommonModule, 16 | FormsModule, 17 | ModalModule.forRoot(), 18 | NgSelectModule, 19 | AngularSlickgridModule.forRoot({ 20 | enableAutoResize: true, 21 | autoHeight: true, 22 | alwaysShowVerticalScroll: false, 23 | autoResize: { 24 | containerId: 'grid-container', 25 | sidePadding: 0 26 | }, 27 | enableFiltering: true, 28 | pagination: { 29 | pageSizes: [], 30 | pageSize: 10, 31 | totalItems: 0 32 | }, 33 | }), 34 | ], 35 | exports: [ 36 | CommonModule, 37 | FormsModule, 38 | ReactiveFormsModule, 39 | ModalModule, 40 | NgSelectModule, 41 | AngularSlickgridModule, 42 | ValidationMessageComponent, 43 | CapitalizePipe, 44 | TrimPipe 45 | ], 46 | declarations: [ 47 | ValidationMessageComponent, 48 | CapitalizePipe, 49 | TrimPipe 50 | ] 51 | }) 52 | export class SharedModule { } 53 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/shared/validation-message/validation-message.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { ValidationService } from 'src/app/core/services/validation.service'; 4 | 5 | @Component({ 6 | selector: 'ar-validation-message', 7 | template: '
{{errorMessage}}
' 8 | }) 9 | export class ValidationMessageComponent { 10 | @Input() control: FormControl; 11 | 12 | constructor(private validationService: ValidationService) { 13 | } 14 | 15 | get errorMessage() { 16 | if (this.control.errors) { 17 | for (let propertyName in this.control.errors) { 18 | if (this.control.errors.hasOwnProperty(propertyName) && this.control.touched) { 19 | return this.validationService.getValidatorErrorMessage(propertyName, this.control.errors[propertyName]); 20 | } 21 | } 22 | } 23 | 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/about/about.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/views/about/about.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/about/about.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

About

5 |
6 |
7 |
8 |
9 |
Created by:
10 | 11 |
12 |
13 |
14 |
Blog:
15 | 16 |
17 |
18 | 22 |
23 |
24 |
25 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-about', 5 | templateUrl: './about.component.html', 6 | styleUrls: ['./about.component.css'] 7 | }) 8 | export class AboutComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/category/category-list/category-list.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/views/category/category-list/category-list.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/category/category-list/category-list.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 8 |
9 |
10 |
11 | 13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/category/category-list/category-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | import { AngularGridInstance, Column, GridOption, GraphqlService, GraphqlResult, Filters, Formatters, OnEventArgs, FieldType } from 'angular-slickgrid'; 7 | 8 | import { CategoryDataService } from 'src/app/core/services/category-data.service'; 9 | import { PageService } from 'src/app/core/services/page.service'; 10 | 11 | 12 | const GRAPHQL_QUERY_DATASET_NAME = 'categories'; 13 | 14 | 15 | @Component({ 16 | templateUrl: './category-list.component.html', 17 | styleUrls: ['./category-list.component.css'] 18 | }) 19 | export class CategoryListComponent implements OnInit { 20 | angularGrid: AngularGridInstance; 21 | columnDefinitions: Column[]; 22 | gridOptions: GridOption; 23 | dataset = []; 24 | 25 | constructor(private dataService: CategoryDataService, private router: Router, private pageService: PageService) { 26 | } 27 | 28 | ngOnInit(): void { 29 | this.columnDefinitions = [ 30 | { 31 | id: 'edit', 32 | field: 'id', 33 | excludeFromColumnPicker: true, 34 | excludeFromGridMenu: true, 35 | excludeFromHeaderMenu: true, 36 | formatter: Formatters.editIcon, 37 | minWidth: 30, 38 | maxWidth: 30, 39 | onCellClick: (e: Event, args: OnEventArgs) => { 40 | this.router.navigate(['/category/category-detail/' + args.dataContext.id]); 41 | } 42 | }, 43 | { id: 'id', field: 'id', name: 'Id', filterable: true, sortable: true, maxWidth: 200, type: FieldType.number, filter: { model: Filters.inputNumber } }, 44 | { id: 'name', field: 'name', name: 'Name', filterable: true, sortable: true }, 45 | { id: 'description', field: 'description', name: 'description', filterable: true, sortable: true }, 46 | ]; 47 | 48 | this.gridOptions = { 49 | backendServiceApi: { 50 | service: new GraphqlService(), 51 | options: { 52 | columnDefinitions: this.columnDefinitions, 53 | datasetName: GRAPHQL_QUERY_DATASET_NAME 54 | }, 55 | process: (query) => this.getCategories(), 56 | } 57 | };; 58 | } 59 | 60 | angularGridReady(angularGrid: AngularGridInstance) { 61 | this.angularGrid = angularGrid; 62 | } 63 | 64 | getCategories(): Observable { 65 | var args = this.pageService.getPageArgs(this.angularGrid); 66 | 67 | return this.dataService.searchCategories(args) 68 | .pipe(map( 69 | page => { 70 | var result: GraphqlResult = { 71 | data: { 72 | [GRAPHQL_QUERY_DATASET_NAME]: { 73 | nodes: page.items, 74 | pageInfo: { 75 | hasNextPage: page.hasNextPage 76 | }, 77 | totalCount: page.totalCount 78 | } 79 | } 80 | }; 81 | 82 | return result; 83 | })); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/category/category-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { CategoryListComponent } from './category-list/category-list.component'; 5 | 6 | const routes: Routes = [ 7 | { 8 | path: '', 9 | data: { title: 'Category' }, 10 | children: [ 11 | { path: '', redirectTo: 'category-list' }, 12 | { path: 'category-list', component: CategoryListComponent, data: { title: 'List' } }, 13 | ] 14 | } 15 | ]; 16 | 17 | @NgModule({ 18 | imports: [RouterModule.forChild(routes)], 19 | exports: [RouterModule] 20 | }) 21 | export class CategoryRoutingModule { 22 | static components = [CategoryListComponent]; 23 | } 24 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/category/category.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { SharedModule } from 'src/app/shared/shared.module'; 4 | import { CategoryRoutingModule } from './category-routing.module'; 5 | 6 | @NgModule({ 7 | imports: [ 8 | CategoryRoutingModule, 9 | SharedModule, 10 | ], 11 | declarations: [CategoryRoutingModule.components] 12 | }) 13 | export class CategoryModule { } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-cart/customer-cart.component.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .quantity { 4 | float: left; 5 | margin-right: 15px; 6 | background-color: #eee; 7 | position: relative; 8 | width: 80px; 9 | overflow: hidden 10 | } 11 | 12 | .quantity input { 13 | margin: 0; 14 | text-align: center; 15 | width: 15px; 16 | height: 15px; 17 | padding: 0; 18 | float: right; 19 | color: #000; 20 | font-size: 20px; 21 | border: 0; 22 | outline: 0; 23 | background-color: #F6F6F6 24 | } 25 | 26 | .quantity input.qty { 27 | position: relative; 28 | border: 0; 29 | width: 100%; 30 | height: 40px; 31 | padding: 10px 25px 10px 10px; 32 | text-align: center; 33 | font-weight: 400; 34 | font-size: 15px; 35 | border-radius: 0; 36 | background-clip: padding-box 37 | } 38 | 39 | .quantity .minus, .quantity .plus { 40 | line-height: 0; 41 | background-clip: padding-box; 42 | -webkit-border-radius: 0; 43 | -moz-border-radius: 0; 44 | border-radius: 0; 45 | -webkit-background-size: 6px 30px; 46 | -moz-background-size: 6px 30px; 47 | color: #bbb; 48 | font-size: 20px; 49 | position: absolute; 50 | height: 50%; 51 | border: 0; 52 | right: 0; 53 | padding: 0; 54 | width: 25px; 55 | z-index: 3 56 | } 57 | 58 | .quantity .minus:hover, .quantity .plus:hover { 59 | background-color: #dad8da 60 | } 61 | 62 | .quantity .minus { 63 | bottom: 0 64 | } 65 | .shopping-cart { 66 | margin-top: 20px; 67 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-cart/customer-cart.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-customer-cart', 5 | templateUrl: './customer-cart.component.html', 6 | styleUrls: ['./customer-cart.component.css'] 7 | }) 8 | export class CustomerCartComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-detail/customer-detail.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
6 |
7 | 21 |
22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 |

30 | {{ customer.firstName | capitalize }} {{ customer.lastName | capitalize }}  31 |

32 |
33 | {{ customer.address }} 34 |
35 | {{ customer.city }}, {{ customer.address }} 36 |
37 |
38 |

39 | 48 |
49 |
50 | No customer found 51 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-detail/customer-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ICustomer } from 'src/app/shared/interfaces'; 3 | import { ActivatedRoute, Params } from '@angular/router'; 4 | import { CustomerDataService } from 'src/app/core/services/customer-data.services'; 5 | 6 | @Component({ 7 | selector: 'app-customer-detail', 8 | templateUrl: './customer-detail.component.html', 9 | styleUrls: ['./customer-detail.component.css'] 10 | }) 11 | export class CustomerDetailComponent implements OnInit { 12 | 13 | customer: ICustomer 14 | 15 | constructor(private route: ActivatedRoute, private dataService: CustomerDataService) { } 16 | 17 | ngOnInit() { 18 | 19 | this.route.params.subscribe((params: Params) => { 20 | const id = +params['id']; 21 | if (id) { 22 | this.dataService.getCustomerById(id) 23 | .subscribe((customer: ICustomer) => { 24 | this.customer = customer; 25 | }); 26 | } 27 | }); 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-edit/customer-edit.component.css: -------------------------------------------------------------------------------- 1 | .customer-form input[type='text'], 2 | .customer-form input[type='number'], 3 | .customer-form input[type='email'], 4 | .customer-form select { 5 | width:100%; 6 | } 7 | 8 | .customer-form .ng-invalid { 9 | border-left: 5px solid #a94442; 10 | } 11 | 12 | .customer-form .ng-valid { 13 | border-left: 5px solid #42A948; 14 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-edit/customer-edit.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 | 6 |
First Name is required
7 |
8 |
9 | 10 | 11 |
Last Name is required
12 |
13 |
14 | 15 | 16 |
Address is required
17 |
18 |
19 | 20 | 21 |
City is required
22 |
23 | 32 |
33 |
34 | Delete Customer?     35 | 36 |
37 |    38 | 39 |
40 |    41 | 42 |
43 |
44 |
{{ errorMessage }}
45 |
46 |
47 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-edit/customer-edit.component.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Component, OnInit, ViewChild } from '@angular/core'; 3 | import { Router, ActivatedRoute, Params } from '@angular/router'; 4 | import { NgForm } from '@angular/forms'; 5 | 6 | import { ICustomer, IState } from 'src/app/shared/interfaces'; 7 | import { CustomerDataService } from 'src/app/core/services/customer-data.services'; 8 | import { LoggerService } from 'src/app/core/services/logger.service'; 9 | 10 | @Component({ 11 | templateUrl: './customer-edit.component.html', 12 | styleUrls: ['./customer-edit.component.css'] 13 | }) 14 | export class CustomerEditComponent implements OnInit { 15 | 16 | customer: ICustomer = 17 | { 18 | id: 0, 19 | firstName: '', 20 | lastName: '', 21 | gender: '', 22 | address: '', 23 | city: '', 24 | state: { 25 | abbreviation: '', 26 | name: '' 27 | } 28 | }; 29 | 30 | states: IState[]; 31 | errorMessage: string; 32 | deleteMessageEnabled: boolean; 33 | operationText = 'Insert'; 34 | @ViewChild('customerForm', { static : true }) customerForm: NgForm; 35 | 36 | constructor(private router: Router, 37 | private route: ActivatedRoute, 38 | private dataService: CustomerDataService, 39 | private logger: LoggerService) { } 40 | 41 | ngOnInit() { 42 | this.route.params.subscribe((params: Params) => { 43 | const id = +params['id']; 44 | if (id !== 0) { 45 | this.operationText = 'Update'; 46 | this.getCustomer(id); 47 | } 48 | }); 49 | } 50 | 51 | getCustomer(id: number) { 52 | this.dataService.getCustomerById(id).subscribe((customer : ICustomer) => { 53 | this.customer = customer 54 | }); 55 | } 56 | 57 | cancel(event: Event) { 58 | event.preventDefault(); 59 | this.router.navigate(['/customer']); 60 | } 61 | 62 | delete(event: Event) { 63 | event.preventDefault(); 64 | 65 | this.dataService.deleteCustomer(this.customer.id) 66 | .subscribe((status: boolean) => { 67 | if (status) { 68 | this.router.navigate(['/customer']); 69 | } else { 70 | this.errorMessage = 'Unable to delete customer'; 71 | } 72 | }, 73 | (err) => this.logger.log(err)); 74 | } 75 | 76 | submit() { 77 | if (this.customer.id === 0) { 78 | this.router.navigate(['/customer']); 79 | } else { 80 | this.router.navigate(['/customer']); 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/customer-grid/customer-grid.component.css: -------------------------------------------------------------------------------- 1 | .grid-container div { 2 | padding-left: 0px; 3 | } 4 | 5 | .grid-container td { 6 | vertical-align: middle; 7 | } 8 | 9 | .grid-image { 10 | height:50px;width:50px;margin-top:10px; 11 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/customer-grid/customer-grid.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
 First NameLast NameAddressCityStateOrder Total 
Customer Image{{ customer.firstName | capitalize }}{{ customer.lastName | capitalize }}{{ customer.address }}{{ customer.city | trim }}{{ customer.state.name }}{{ customer.orderTotal | currency:'USD':'symbol' }}View Orders
 No Records Found
36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/customer-grid/customer-grid.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; 2 | import { ICustomer } from 'src/app/shared/interfaces'; 3 | 4 | @Component({ 5 | selector: 'cm-customer-grid', 6 | templateUrl: './customer-grid.component.html', 7 | styleUrls: ['./customer-grid.component.css'] 8 | //changeDetection: ChangeDetectionStrategy.OnPush 9 | }) 10 | export class CustomerGridComponent implements OnInit { 11 | 12 | @Input() customers: ICustomer[] 13 | 14 | constructor() { } 15 | 16 | ngOnInit() { 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/customer-list.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

5 | 6 | {{ title }} 7 |

8 |
9 |
10 |
11 |
12 | 25 |
26 |
27 | 28 | 30 | 31 | 36 | 37 |
38 |
39 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/customer-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ICustomer } from 'src/app/shared/interfaces'; 3 | import { CustomerDataService } from 'src/app/core/services/customer-data.services'; 4 | 5 | @Component({ 6 | selector: 'app-customer-list', 7 | templateUrl: './customer-list.component.html', 8 | styleUrls: ['./customer-list.component.css'] 9 | }) 10 | export class CustomerListComponent implements OnInit { 11 | 12 | title: string 13 | filterText: string 14 | customers: ICustomer[] = [] 15 | filteredCustomers: ICustomer[] = [] 16 | 17 | totalRecords: number = 0; 18 | pageSize: number = 10; 19 | 20 | constructor(private dataService: CustomerDataService) { } 21 | 22 | ngOnInit() { 23 | this.title = 'Customers' 24 | this.filterText = '' 25 | 26 | this.getCustomers(); 27 | //this.getCustomersPage(1); 28 | } 29 | 30 | getCustomers() { 31 | this.dataService.getCustomers().subscribe((customers: ICustomer[]) => { 32 | this.customers = customers; 33 | this.filteredCustomers = customers; 34 | }); 35 | } 36 | 37 | filterChanged(data: string) { 38 | if (data && this.customers) { 39 | data = data.toLocaleUpperCase(); 40 | this.filteredCustomers = this.customers.filter(customer => customer.firstName.toLocaleUpperCase().indexOf(data) > -1); 41 | } else { 42 | this.filteredCustomers = this.customers; 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/filter-textbox/filter-textbox.component.css: -------------------------------------------------------------------------------- 1 | cm-filter-textbox { 2 | margin-top: 5px; 3 | } 4 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/filter-textbox/filter-textbox.component.html: -------------------------------------------------------------------------------- 1 |
2 | Filter: 3 | 6 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-list/filter-textbox/filter-textbox.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'cm-filter-textbox', 5 | templateUrl: './filter-textbox.component.html', 6 | styleUrls: ['./filter-textbox.component.css'] 7 | }) 8 | export class FilterTextboxComponent { 9 | 10 | filterText: string 11 | 12 | @Output() 13 | changed: EventEmitter = new EventEmitter(); 14 | 15 | filterChanged(event: any) { 16 | event.preventDefault(); 17 | this.changed.emit(this.filterText); // Raise changed event 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-order/customer-order.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/views/customer/customer-order/customer-order.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-order/customer-order.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Orders for {{ customer.firstName | capitalize }} {{ customer.lastName | capitalize }}

4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
{{ order.productName }}{{ order.itemCost | currency:'USD':'symbol' }}
 {{ customer.orderTotal | currency:'USD':'symbol' }}
15 |
16 |
17 | No orders found 18 |
19 |
20 | No customer found 21 |
22 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-order/customer-order.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { IOrder, ICustomer } from 'src/app/shared/interfaces'; 3 | import { CustomerDataService } from 'src/app/core/services/customer-data.services'; 4 | import { ActivatedRoute, Params } from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-customer-order', 8 | templateUrl: './customer-order.component.html', 9 | styleUrls: ['./customer-order.component.css'] 10 | }) 11 | export class CustomerOrderComponent implements OnInit { 12 | 13 | orders: IOrder[] = []; 14 | customer: ICustomer; 15 | 16 | constructor(private route: ActivatedRoute, private dataService: CustomerDataService) { } 17 | 18 | ngOnInit() { 19 | this.route.params.subscribe((params: Params) => { 20 | const id = +params['id']; 21 | this.dataService.getCustomerById(id).subscribe((customer: ICustomer) => { 22 | this.customer = customer; 23 | }); 24 | }); 25 | } 26 | 27 | ordersTrackBy(index: number, orderItem: any) { 28 | return index; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import { CustomerListComponent } from './customer-list/customer-list.component'; 5 | import { CustomerDetailComponent } from './customer-detail/customer-detail.component'; 6 | import { CustomerEditComponent } from './customer-edit/customer-edit.component'; 7 | import { CustomerOrderComponent } from './customer-order/customer-order.component'; 8 | import { CustomerCartComponent } from './customer-cart/customer-cart.component'; 9 | 10 | const routes: Routes = [ 11 | { 12 | path: '', 13 | data: { title: 'Customer' }, 14 | children: [ 15 | { path: '', redirectTo: 'customer-list' }, 16 | { path: 'customer-list', component: CustomerListComponent, data: { title: 'List' } }, 17 | { path: 'customer-cart', component: CustomerCartComponent, data: { title: 'List' } }, 18 | { path: 'customer-detail/:id', component: CustomerDetailComponent, data: { title: 'Detail' } }, 19 | { path: 'customer-edit', component: CustomerEditComponent, data: { title: 'New' } }, 20 | { path: 'customer-edit/:id', component: CustomerEditComponent, data: { title: 'Edit' } }, 21 | { path: 'customer-order', component: CustomerOrderComponent, data: { title: 'New' } }, 22 | { path: 'customer-order/:id', component: CustomerOrderComponent, data: { title: 'Edit' } }, 23 | ] 24 | } 25 | ]; 26 | 27 | @NgModule({ 28 | imports: [RouterModule.forChild(routes)], 29 | exports: [RouterModule] 30 | }) 31 | export class CustomerRoutingModule { 32 | static components = [CustomerListComponent, CustomerDetailComponent, CustomerEditComponent]; 33 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/customer/customer.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { SharedModule } from 'src/app/shared/shared.module'; 4 | import { CustomerRoutingModule } from './customer-routing.module'; 5 | import { FilterTextboxComponent } from './customer-list/filter-textbox/filter-textbox.component'; 6 | import { CustomerGridComponent } from './customer-list/customer-grid/customer-grid.component'; 7 | import { CustomerOrderComponent } from './customer-order/customer-order.component'; 8 | import { CustomerCartComponent } from './customer-cart/customer-cart.component'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CustomerRoutingModule, 13 | SharedModule, 14 | ], 15 | declarations: [CustomerRoutingModule.components, FilterTextboxComponent, CustomerGridComponent, CustomerOrderComponent, CustomerCartComponent] 16 | }) 17 | export class CustomerModule { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 | 10 | Step 1 content 11 | 12 | 13 | 14 | Step 2 content 15 | 16 | 17 | 18 | Step 3 content 19 | 20 | 21 | 22 | Step 4 content 23 | 24 | 25 | 26 | Step 5 content 27 | 28 | 29 | 30 |
31 | 32 |
33 |
34 |
35 | 39 | 43 | 45 | 48 |
49 |
50 |
51 | 52 |
53 |
54 |
{{ stepChangedArgs | json }}
55 |
56 |
57 |
58 |
59 |
60 |
61 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { STEP_STATE, NgWizardConfig, THEME, StepChangedArgs, NgWizardService } from 'ng-wizard'; 3 | 4 | @Component({ 5 | templateUrl: 'dashboard.component.html' 6 | }) 7 | export class DashboardComponent implements OnInit { 8 | 9 | stepStates = { normal: STEP_STATE.normal, disabled: STEP_STATE.disabled, error: STEP_STATE.error, hidden: STEP_STATE.hidden }; 10 | 11 | config: NgWizardConfig = { 12 | selected: 0, 13 | theme: THEME.arrows, 14 | toolbarSettings: { 15 | toolbarExtraButtons: [ 16 | { text: 'Finish', class: 'btn btn-info', event: () => { alert("Finished!!!"); } }, 17 | { text: 'Reset', class: 'btn btn-danger', event: () => { this.resetWizard(); } }] 18 | } 19 | }; 20 | 21 | stepChangedArgs: StepChangedArgs; 22 | selectedtheme: THEME; 23 | themes = [THEME.default, THEME.arrows, THEME.circles, THEME.dots]; 24 | 25 | constructor(private ngWizardService: NgWizardService) { 26 | } 27 | 28 | ngOnInit() { 29 | this.selectedtheme = this.config.theme; 30 | } 31 | 32 | showPreviousStep(event?: Event) { 33 | this.ngWizardService.previous(); 34 | } 35 | 36 | showNextStep(event?: Event) { 37 | this.ngWizardService.next(); 38 | } 39 | 40 | resetWizard(event?: Event) { 41 | this.selectedtheme = this.config.theme; 42 | this.ngWizardService.reset(); 43 | } 44 | 45 | themeSelected() { 46 | this.ngWizardService.theme(this.selectedtheme); 47 | } 48 | 49 | stepChanged(args: StepChangedArgs) { 50 | console.log(args.step); 51 | this.stepChangedArgs = args; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/error/404.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |

404

7 |

Oops! You're lost.

8 |

The page you are looking for was not found.

9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/error/404.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: '404.component.html' 5 | }) 6 | export class P404Component { 7 | 8 | constructor() { } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/error/500.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |

500

7 |

Houston, we have a problem!

8 |

The page you are looking for is temporarily unavailable.

9 |
10 |
11 |
12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/error/500.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | templateUrl: '500.component.html' 5 | }) 6 | export class P500Component { 7 | 8 | constructor() { } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |

Login

11 |

Sign In to your account

12 |
13 |
14 | 15 |
16 | 18 |
19 |
20 |
21 | 22 |
23 | 25 |
26 |
27 |
28 | 29 | 30 |
31 |
32 |
33 |
34 | 36 |
37 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormGroup, FormBuilder } from '@angular/forms'; 3 | import { Router, ActivatedRoute } from '@angular/router'; 4 | 5 | import { ValidationService } from 'src/app/core/services/validation.service'; 6 | import { AuthService } from 'src/app/core/services/auth.service'; 7 | 8 | @Component({ 9 | selector: 'app-dashboard', 10 | templateUrl: 'login.component.html' 11 | }) 12 | export class LoginComponent implements OnInit { 13 | returnUrl: string; 14 | loginForm: FormGroup; 15 | 16 | constructor( 17 | private authService: AuthService, 18 | private validationService: ValidationService, 19 | private formBuilder: FormBuilder, 20 | private route: ActivatedRoute, 21 | private router: Router) { 22 | this.initializeForm(); 23 | } 24 | 25 | ngOnInit() { 26 | this.authService.logout(); 27 | this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; 28 | } 29 | 30 | initializeForm() { 31 | this.loginForm = this.formBuilder.group({ 32 | email: ['', [this.validationService.email]], 33 | password: ['', [this.validationService.password]] 34 | }); 35 | } 36 | 37 | login() { 38 | const farmValue = this.loginForm.value; 39 | 40 | if (farmValue.email && farmValue.password) { 41 | this.authService.login(farmValue.email, farmValue.password) 42 | .subscribe(() => { 43 | this.router.navigateByUrl(this.returnUrl); 44 | }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-delete/product-delete-modal.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/views/product/product-delete/product-delete-modal.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-delete/product-delete-modal.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-delete/product-delete-modal.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild, Input, Output, EventEmitter } from '@angular/core'; 2 | import { ModalDirective } from 'ngx-bootstrap/modal'; 3 | import { ProductDataService } from 'src/app/core/services/product-data.service'; 4 | 5 | @Component({ 6 | selector: 'product-delete-modal', 7 | templateUrl: './product-delete-modal.component.html', 8 | styleUrls: ['./product-delete-modal.component.css'] 9 | }) 10 | export class ProductDeleteModalComponent implements OnInit { 11 | @ViewChild('deleteModal', { static: true }) public deleteModal: ModalDirective; 12 | @Input() productId?: number; 13 | @Output() productDeleted: EventEmitter = new EventEmitter(); 14 | 15 | constructor(private dataService: ProductDataService) { 16 | } 17 | 18 | ngOnInit() { 19 | } 20 | 21 | show() { 22 | this.deleteModal.show(); 23 | } 24 | 25 | hide() { 26 | this.deleteModal.hide(); 27 | } 28 | 29 | delete() { 30 | this.dataService.deleteProductById(this.productId).subscribe(() => { 31 | this.productDeleted.emit(); 32 | this.hide(); 33 | }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-detail/product-detail.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/views/product/product-detail/product-detail.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-detail/product-detail.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 8 | 10 | 11 | 12 | 14 |
15 |
16 | 17 |
18 |
19 |
20 |
21 | Product Info 22 |
23 |
24 | 25 |
26 | 27 |
28 | 30 |
31 |
32 | 33 |
34 | 35 |
36 | 38 |
39 |
40 | 41 |
42 | 43 |
44 | 46 |
47 |
48 | 49 |
50 | 51 |
52 | 54 |
55 |
56 | 57 |
58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 |
66 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-detail/product-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { IProduct } from 'src/app/shared/interfaces'; 3 | import { Router, ActivatedRoute } from '@angular/router'; 4 | import { ProductDataService } from 'src/app/core/services/product-data.service'; 5 | 6 | @Component({ 7 | templateUrl: './product-detail.component.html', 8 | styleUrls: ['./product-detail.component.css'] 9 | }) 10 | export class ProductDetailComponent implements OnInit { 11 | 12 | product: IProduct = { 13 | id: null, 14 | name: '', 15 | description: '', 16 | unitPrice: 0, 17 | category: { id: null } 18 | }; 19 | 20 | constructor(private dataService: ProductDataService, private router: Router, private route: ActivatedRoute) { 21 | route.params.subscribe(val => { 22 | const id = +this.route.snapshot.paramMap.get('id'); 23 | 24 | if (id !== undefined && id != null && id !== 0) { 25 | this.dataService.getProductById(id).subscribe((product: IProduct) => { 26 | if (product != null) { 27 | this.product = product; 28 | } 29 | else { 30 | this.router.navigate(['/']); 31 | } 32 | }); 33 | } 34 | else { 35 | this.router.navigate(['/']); 36 | } 37 | }); 38 | } 39 | 40 | ngOnInit() { 41 | } 42 | 43 | close() { 44 | this.router.navigate(['/product/product-list']); 45 | } 46 | 47 | update() { 48 | this.router.navigate(['/product/product-edit/' + this.product.id]); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-edit/product-edit.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/views/product/product-edit/product-edit.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-edit/product-edit.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ICategory, IProduct } from 'src/app/shared/interfaces'; 3 | import { FormGroup, FormBuilder, Validators } from '@angular/forms'; 4 | import { ProductDataService } from 'src/app/core/services/product-data.service'; 5 | import { CategoryDataService } from 'src/app/core/services/category-data.service'; 6 | import { ActivatedRoute, Router } from '@angular/router'; 7 | import { forkJoin } from 'rxjs'; 8 | 9 | @Component({ 10 | templateUrl: './product-edit.component.html', 11 | styleUrls: ['./product-edit.component.css'] 12 | }) 13 | export class ProductEditComponent implements OnInit { 14 | 15 | // selectedFormValues: { category?: ICategory } = {}; 16 | categories: ICategory[] = []; 17 | productForm: FormGroup; 18 | operationText: string = "Create"; 19 | 20 | constructor( 21 | private productDataService: ProductDataService, 22 | private categoryDataService: CategoryDataService, 23 | private formBuilder: FormBuilder, 24 | private router: Router, 25 | private route: ActivatedRoute) { 26 | route.params.subscribe(val => { 27 | this.initializeForm(); 28 | 29 | const id = +this.route.snapshot.paramMap.get('id'); 30 | 31 | if (id !== undefined && id != null && id !== 0) { 32 | 33 | this.productDataService.getProductById(id).subscribe((product: IProduct) => { 34 | if (product != null) { 35 | this.productForm.patchValue(product); 36 | 37 | let categories = this.categoryDataService.getCategories(); 38 | 39 | forkJoin([categories]).subscribe(results => { 40 | this.setFormCategoryData(results[0]); 41 | }); 42 | 43 | this.operationText = 'Update'; 44 | } 45 | else { 46 | this.router.navigate(['/product/product-edit']); 47 | } 48 | }); 49 | } 50 | else { 51 | let categories = this.categoryDataService.getCategories(); 52 | 53 | forkJoin([categories]).subscribe(results => { 54 | this.setFormCategoryData(results[0]); 55 | }); 56 | } 57 | }); 58 | } 59 | 60 | ngOnInit() { 61 | } 62 | 63 | initializeForm() { 64 | // this.selectedFormValues = {}; 65 | this.categories = []; 66 | this.productForm = this.formBuilder.group({ 67 | id: [0], 68 | name: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(30)]], 69 | description: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(30)]], 70 | unitPrice: [0, [Validators.required]], 71 | categoryId: [0, Validators.required], 72 | selectedcategory: [], 73 | }); 74 | } 75 | 76 | setFormCategoryData(categories: ICategory[]) { 77 | this.categories = categories; 78 | 79 | var productCategoryId = this.productForm.get('categoryId').value; 80 | var productCategory = this.categories.filter(c => c.id === productCategoryId)[0]; 81 | this.productForm.get('selectedcategory').patchValue(productCategory); 82 | } 83 | 84 | onCategorySelected() { 85 | var productCategory = this.productForm.get('selectedcategory').value; 86 | var productCategoryId = productCategory ? productCategory.id : 0; 87 | this.productForm.get('categoryId').patchValue(productCategoryId); 88 | } 89 | 90 | saveProduct(product: IProduct) { 91 | if (product.id > 0) { 92 | this.productDataService.updateProduct(product).subscribe(() => { 93 | this.router.navigate(['/product/product-detail/' + product.id]); 94 | }); 95 | } 96 | else { 97 | this.productDataService.createProduct(product).subscribe((savedProduct: IProduct) => { 98 | this.router.navigate(['/product/product-detail/' + savedProduct.id]); 99 | }); 100 | } 101 | } 102 | 103 | close() { 104 | this.router.navigate(['/product/product-list']); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-list/product-list.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/app/views/product/product-list/product-list.component.css -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-list/product-list.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 8 |
9 |
10 |
11 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |
-------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-list/product-list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | 6 | import { AngularGridInstance, Column, GridOption, GraphqlService, GraphqlResult, Filters, Formatters, OnEventArgs, FieldType } from 'angular-slickgrid'; 7 | 8 | import { ProductDataService } from 'src/app/core/services/product-data.service'; 9 | import { PageService } from 'src/app/core/services/page.service'; 10 | 11 | 12 | const GRAPHQL_QUERY_DATASET_NAME = 'products'; 13 | 14 | 15 | @Component({ 16 | templateUrl: './product-list.component.html', 17 | styleUrls: ['./product-list.component.css'] 18 | }) 19 | export class ProductListComponent implements OnInit { 20 | angularGrid: AngularGridInstance; 21 | columnDefinitions: Column[]; 22 | gridOptions: GridOption; 23 | dataset = []; 24 | 25 | constructor(private dataService: ProductDataService, private router: Router, private pageService: PageService) { 26 | } 27 | 28 | ngOnInit(): void { 29 | this.columnDefinitions = [ 30 | { 31 | id: 'edit', 32 | field: 'id', 33 | excludeFromColumnPicker: true, 34 | excludeFromGridMenu: true, 35 | excludeFromHeaderMenu: true, 36 | formatter: Formatters.editIcon, 37 | minWidth: 30, 38 | maxWidth: 30, 39 | onCellClick: (e: Event, args: OnEventArgs) => { 40 | this.router.navigate(['/product/product-detail/' + args.dataContext.id]); 41 | } 42 | }, 43 | { id: 'id', field: 'id', name: 'Id', filterable: true, sortable: true, maxWidth: 200, type: FieldType.number, filter: { model: Filters.inputNumber } }, 44 | { id: 'name', field: 'name', name: 'Name', filterable: true, sortable: true }, 45 | { id: 'unitPrice', field: 'unitPrice', name: 'Unit Price', filterable: true, sortable: true, maxWidth: 200, filter: { model: Filters.inputNumber } }, 46 | { id: 'category', field: 'category.name', name: 'Category', filterable: true, sortable: true, formatter: Formatters.complexObject }, 47 | ]; 48 | 49 | this.gridOptions = { 50 | backendServiceApi: { 51 | service: new GraphqlService(), 52 | options: { 53 | columnDefinitions: this.columnDefinitions, 54 | datasetName: GRAPHQL_QUERY_DATASET_NAME 55 | }, 56 | process: (query) => this.getProducts(), 57 | } 58 | };; 59 | } 60 | 61 | angularGridReady(angularGrid: AngularGridInstance) { 62 | this.angularGrid = angularGrid; 63 | } 64 | 65 | getProducts(): Observable { 66 | var args = this.pageService.getPageArgs(this.angularGrid); 67 | 68 | return this.dataService.searchProducts(args) 69 | .pipe(map( 70 | page => { 71 | var result: GraphqlResult = { 72 | data: { 73 | [GRAPHQL_QUERY_DATASET_NAME]: { 74 | nodes: page.items, 75 | pageInfo: { 76 | hasNextPage: page.hasNextPage 77 | }, 78 | totalCount: page.totalCount 79 | } 80 | } 81 | }; 82 | 83 | return result; 84 | })); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { ProductListComponent } from './product-list/product-list.component'; 5 | import { ProductDetailComponent } from './product-detail/product-detail.component'; 6 | import { ProductEditComponent } from './product-edit/product-edit.component'; 7 | import { ProductDeleteModalComponent } from './product-delete/product-delete-modal.component'; 8 | 9 | const routes: Routes = [ 10 | { 11 | path: '', 12 | data: { title: 'Product' }, 13 | children: [ 14 | { path: '', redirectTo: 'product-list' }, 15 | { path: 'product-list', component: ProductListComponent, data: { title: 'List' } }, 16 | { path: 'product-detail/:id', component: ProductDetailComponent, data: { title: 'Detail' } }, 17 | { path: 'product-edit', component: ProductEditComponent, data: { title: 'New' } }, 18 | { path: 'product-edit/:id', component: ProductEditComponent, data: { title: 'Edit' } }, 19 | ] 20 | } 21 | ]; 22 | 23 | @NgModule({ 24 | imports: [RouterModule.forChild(routes)], 25 | exports: [RouterModule] 26 | }) 27 | export class ProductRoutingModule { 28 | static components = [ProductListComponent, ProductDetailComponent, ProductEditComponent, ProductDeleteModalComponent]; 29 | } 30 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/product/product.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | 3 | import { SharedModule } from 'src/app/shared/shared.module'; 4 | import { ProductRoutingModule } from './product-routing.module'; 5 | 6 | @NgModule({ 7 | imports: [ 8 | ProductRoutingModule, 9 | SharedModule, 10 | ], 11 | declarations: [ProductRoutingModule.components] 12 | }) 13 | export class ProductModule { } 14 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/register/register.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |

Register

10 |

Create your account

11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 | @ 20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 |
33 | 34 |
35 | 36 |
37 |
38 | 48 |
49 |
50 |
51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/app/views/register/register.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-dashboard', 5 | templateUrl: 'register.component.html' 6 | }) 7 | export class RegisterComponent { 8 | 9 | constructor() { } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/assets/img/brand/sygnet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | 11 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/assets/img/female.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/assets/img/female.png -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/assets/img/male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/assets/img/male.png -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/assets/img/people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/assets/img/people.png -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | apiUrl: 'http://localhost:64167/api', 4 | }; 5 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | apiUrl: 'http://localhost:64167/api', 8 | }; 9 | 10 | /* 11 | * For easier debugging in development mode, you can import the following file 12 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 13 | * 14 | * This import should be commented out in production mode because it will have a negative impact 15 | * on performance if an error is thrown. 16 | */ 17 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 18 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnetrun/run-aspnetcore-cqrs/338911bd298cd11832acbebd82e2f067983846f9/src/AspnetRun.Web/src/favicon.ico -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AspnetRun 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage/AspnetRun'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/styles.scss: -------------------------------------------------------------------------------- 1 | @import '~@ng-select/ng-select/themes/default.theme.css'; 2 | 3 | @import "~simple-line-icons/css/simple-line-icons.css"; 4 | 5 | @import "~@coreui/coreui/scss/coreui"; 6 | 7 | @import '~ng-wizard/themes/ng_wizard.min.css'; 8 | @import '~ng-wizard/themes/ng_wizard_theme_arrows.min.css'; 9 | @import '~ng-wizard/themes/ng_wizard_theme_circles.min.css'; 10 | @import '~ng-wizard/themes/ng_wizard_theme_dots.min.css'; 11 | 12 | @import '~ngx-toastr/toastr-bs4-alert'; 13 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/AspnetRun.Web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "downlevelIteration": true, 6 | "outDir": "./dist/out-tsc", 7 | "sourceMap": true, 8 | "declaration": false, 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "target": "es5", 15 | "typeRoots": [ 16 | "node_modules/@types" 17 | ], 18 | "lib": [ 19 | "es2018", 20 | "dom" 21 | ] 22 | } 23 | } -------------------------------------------------------------------------------- /src/AspnetRun.Web/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "array-type": false, 8 | "arrow-parens": false, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs/Rx" 15 | ], 16 | "interface-name": false, 17 | "max-classes-per-file": false, 18 | "max-line-length": [ 19 | true, 20 | 140 21 | ], 22 | "member-access": false, 23 | "member-ordering": [ 24 | true, 25 | { 26 | "order": [ 27 | "static-field", 28 | "instance-field", 29 | "static-method", 30 | "instance-method" 31 | ] 32 | } 33 | ], 34 | "no-consecutive-blank-lines": false, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-empty": false, 44 | "no-inferrable-types": [ 45 | true, 46 | "ignore-params" 47 | ], 48 | "no-non-null-assertion": true, 49 | "no-redundant-jsdoc": true, 50 | "no-switch-case-fall-through": true, 51 | "no-use-before-declare": true, 52 | "no-var-requires": false, 53 | "object-literal-key-quotes": [ 54 | true, 55 | "as-needed" 56 | ], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [ 60 | true, 61 | "single" 62 | ], 63 | "trailing-comma": false, 64 | "no-output-on-prefix": true, 65 | "no-inputs-metadata-property": true, 66 | "no-outputs-metadata-property": true, 67 | "no-host-metadata-property": true, 68 | "no-input-rename": true, 69 | "no-output-rename": true, 70 | "use-lifecycle-interface": true, 71 | "use-pipe-transform-interface": true, 72 | "component-class-suffix": true, 73 | "directive-class-suffix": true 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/AspnetRun.Tests/AspnetRun.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.2 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /test/AspnetRun.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace AspnetRun.Tests 5 | { 6 | public class UnitTest1 7 | { 8 | [Fact] 9 | public void Test1() 10 | { 11 | 12 | } 13 | } 14 | } 15 | --------------------------------------------------------------------------------