├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── codacy-analysis.yml ├── .gitignore ├── APIGateway └── CMAPIGateway │ ├── CMAPIGateway.csproj │ ├── Dockerfile │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── ocelot.json ├── Course ├── db │ ├── Dockerfile │ └── data │ │ ├── SqlCmdStartup.sh │ │ ├── coursedb.sql │ │ └── entrypoint.sh └── src │ ├── Course.API │ ├── Controllers │ │ ├── BaseController.cs │ │ └── CourseController.cs │ ├── Course.API.csproj │ ├── Dockerfile │ ├── Filters │ │ └── ApiExceptionFilterAttribute.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json │ ├── Course.Application │ ├── Common │ │ ├── BaseClass │ │ │ └── ApplicationBase.cs │ │ ├── Behaviors │ │ │ ├── LoggingBehaviour.cs │ │ │ ├── PerformanceBehaviour.cs │ │ │ ├── UnhandledExceptionBehaviour.cs │ │ │ └── ValidationBehaviour.cs │ │ ├── Exceptions │ │ │ ├── NotFoundException.cs │ │ │ └── ValidationException.cs │ │ ├── Interfaces │ │ │ └── IConfigConstants.cs │ │ └── Mappings │ │ │ ├── IMapFrom.cs │ │ │ └── MappingProfile.cs │ ├── Course.Application.csproj │ ├── Course │ │ ├── Commands │ │ │ ├── AddCourseCommand.cs │ │ │ ├── AddCourseCommandValidator.cs │ │ │ ├── DeleteCourseCommand.cs │ │ │ ├── DeleteCourseCommandValidator.cs │ │ │ ├── UpdateCourseCommand.cs │ │ │ └── UpdateCourseCommandValidator.cs │ │ ├── DTO │ │ │ └── CourseDTO.cs │ │ ├── Queries │ │ │ ├── GetAllCourseQuery.cs │ │ │ ├── GetSingleCourseQuery.cs │ │ │ └── GetSingleCourseQueryValidator.cs │ │ └── VM │ │ │ └── CourseVM.cs │ └── DependencyInjection.cs │ ├── Course.Domain │ ├── Course.Domain.csproj │ ├── Entities │ │ └── Course.cs │ ├── Repositories │ │ └── ICourseRepository.cs │ └── UnitOfWork │ │ └── IUnitOfWork.cs │ └── Course.Persistence │ ├── Constant │ └── ConfigConstants.cs │ ├── Course.Persistence.csproj │ ├── DependencyInjection.cs │ ├── Repositories │ ├── CourseRepository.cs │ └── Repository.cs │ └── UnitOfWork │ └── UnitOfWork.cs ├── CourseBasket └── src │ ├── CourseBasket.API │ ├── Controllers │ │ ├── BaseController.cs │ │ └── CoursesBasketController.cs │ ├── CourseBasket.API.csproj │ ├── Dockerfile │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json │ ├── CourseBasket.Application │ ├── Common │ │ ├── BaseClass │ │ │ └── ApplicationBase.cs │ │ ├── Behaviors │ │ │ ├── LoggingBehaviour.cs │ │ │ ├── PerformanceBehaviour.cs │ │ │ ├── UnhandledExceptionBehaviour.cs │ │ │ └── ValidationBehaviour.cs │ │ ├── Exceptions │ │ │ ├── BadRequestException.cs │ │ │ ├── NotFoundException.cs │ │ │ └── ValidationException.cs │ │ ├── Interfaces │ │ │ └── IConfigConstants.cs │ │ └── Mappings │ │ │ ├── IMapFrom.cs │ │ │ └── MappingProfile.cs │ ├── CourseBasket.Application.csproj │ ├── CoursesBasket │ │ ├── Commands │ │ │ ├── CheckoutBasketCommand.cs │ │ │ ├── DeleteBasketCommand.cs │ │ │ └── UpdateBasketCommand.cs │ │ ├── DTO │ │ │ └── BasketCheckoutDTO.cs │ │ ├── Queries │ │ │ ├── GetAllItemBasketQuery.cs │ │ │ └── GetBasketQuery.cs │ │ └── VM │ │ │ └── CourseBasketVM.cs │ └── DependencyInjection.cs │ ├── CourseBasket.Domain │ ├── CourseBasket.Domain.csproj │ ├── Entities │ │ ├── BasketCart.cs │ │ ├── BasketCartItem.cs │ │ └── BasketCheckout.cs │ ├── Repositories │ │ └── IBasketRepository.cs │ └── UnitOfWork │ │ └── IUnitOfWork.cs │ └── CourseBasket.Persistence │ ├── Constant │ └── ConfigConstants.cs │ ├── CourseBasket.Persistence.csproj │ ├── DependencyInjection.cs │ ├── Repositories │ └── BasketRepository.cs │ └── UnitOfWork │ └── UnitOfWork.cs ├── README.md ├── RegistrationQueue ├── Common │ └── Constants.cs ├── EventBusRabbitMQ.csproj ├── Events │ └── CourseCheckoutEvent.cs ├── Producer │ └── EventBusRabbitMQProducer.cs └── RabbitMQConnection │ ├── IRabbitMQConnection.cs │ └── RabbitMQConnection.cs ├── SCM.Web └── scm │ ├── .browserslistrc │ ├── .dockerignore │ ├── .editorconfig │ ├── .gitignore │ ├── .vscode │ ├── launch.json │ └── tasks.json │ ├── Dockerfile │ ├── README.md │ ├── angular.json │ ├── e2e │ ├── protractor.conf.js │ ├── src │ │ ├── app.e2e-spec.ts │ │ └── app.po.ts │ └── tsconfig.json │ ├── karma.conf.js │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── app │ │ ├── admin │ │ │ ├── admin │ │ │ │ ├── admin.component.css │ │ │ │ ├── admin.component.html │ │ │ │ ├── admin.component.spec.ts │ │ │ │ └── admin.component.ts │ │ │ ├── course │ │ │ │ ├── course-list │ │ │ │ │ ├── course-list.component.css │ │ │ │ │ ├── course-list.component.html │ │ │ │ │ ├── course-list.component.spec.ts │ │ │ │ │ └── course-list.component.ts │ │ │ │ └── manage-course │ │ │ │ │ ├── manage-course.component.css │ │ │ │ │ ├── manage-course.component.html │ │ │ │ │ ├── manage-course.component.spec.ts │ │ │ │ │ └── manage-course.component.ts │ │ │ └── user │ │ │ │ ├── manage-user │ │ │ │ ├── manage-user.component.css │ │ │ │ ├── manage-user.component.html │ │ │ │ ├── manage-user.component.spec.ts │ │ │ │ └── manage-user.component.ts │ │ │ │ └── user-list │ │ │ │ ├── user-list.component.css │ │ │ │ ├── user-list.component.html │ │ │ │ ├── user-list.component.spec.ts │ │ │ │ └── user-list.component.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── client │ │ │ └── select-course │ │ │ │ ├── select-course.component.css │ │ │ │ ├── select-course.component.html │ │ │ │ ├── select-course.component.spec.ts │ │ │ │ └── select-course.component.ts │ │ ├── material-module.ts │ │ ├── model │ │ │ ├── BoughtCourses.ts │ │ │ ├── course.ts │ │ │ ├── courseBasket.ts │ │ │ ├── courseBasketVM.ts │ │ │ ├── selectValue.ts │ │ │ └── user.ts │ │ ├── service │ │ │ └── data │ │ │ │ └── data.service.ts │ │ └── shared │ │ │ ├── enum.ts │ │ │ ├── messages │ │ │ └── confirm-delete │ │ │ │ ├── confirm-delete.component.css │ │ │ │ ├── confirm-delete.component.html │ │ │ │ ├── confirm-delete.component.spec.ts │ │ │ │ └── confirm-delete.component.ts │ │ │ └── util.ts │ ├── assets │ │ ├── .gitkeep │ │ └── images │ │ │ └── banner.jpg │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── tslint.json ├── StudentCourse ├── db │ ├── Dockerfile │ └── data │ │ ├── SqlCmdStartup.sh │ │ ├── entrypoint.sh │ │ └── studentcoursedb.sql └── src │ ├── StudentCourse.API │ ├── Controllers │ │ ├── BaseController.cs │ │ └── StudentControllerController.cs │ ├── Dockerfile │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── StudentCourse.API.csproj │ ├── appsettings.Development.json │ └── appsettings.json │ ├── StudentCourse.Application │ ├── Common │ │ ├── BaseClass │ │ │ └── ApplicationBase.cs │ │ ├── Behaviors │ │ │ ├── LoggingBehaviour.cs │ │ │ ├── PerformanceBehaviour.cs │ │ │ ├── UnhandledExceptionBehaviour.cs │ │ │ └── ValidationBehaviour.cs │ │ ├── Exceptions │ │ │ ├── NotFoundException.cs │ │ │ └── ValidationException.cs │ │ ├── Interfaces │ │ │ └── IConfigConstants.cs │ │ └── Mappings │ │ │ ├── IMapFrom.cs │ │ │ └── MappingProfile.cs │ ├── DependencyInjection.cs │ ├── RabbitMQ │ │ ├── ApplicationBuilderExtentions.cs │ │ └── EventBusRabbitMQConsumer.cs │ ├── StudentCourse.Application.csproj │ └── StudentCourse │ │ ├── Commands │ │ ├── AddStudentCourseCommand.cs │ │ ├── DeleteStudentCourseCommand.cs │ │ ├── DeleteStudentCourseCommandValidator.cs │ │ ├── UpdateStudentCourseCommand.cs │ │ └── UpdateStudentCourseCommandValidator.cs │ │ ├── DTO │ │ └── StudentCourseDTO.cs │ │ ├── Queries │ │ ├── GetAllStudentCourseQuery.cs │ │ ├── GetSingleStudentCourseQuery.cs │ │ └── GetSingleStudentCourseQueryValidator.cs │ │ └── VM │ │ └── StudentCourseVM.cs │ ├── StudentCourse.Domain │ ├── Entities │ │ └── StudentCourse.cs │ ├── Repositories │ │ └── IStudentCourseRepository.cs │ ├── StudentCourse.Domain.csproj │ └── UnitOfWork │ │ └── IUnitOfWork.cs │ └── StudentCourse.Persistence │ ├── Constant │ └── ConfigConstants.cs │ ├── DependencyInjection.cs │ ├── Repositories │ ├── Repository.cs │ └── StudentCourseRepository.cs │ ├── StudentCourse.Persistence.csproj │ └── UnitOfWork │ └── UnitOfWork.cs ├── StudentCourseManagement.sln ├── User ├── db │ ├── Dockerfile │ └── data │ │ ├── SqlCmdStartup.sh │ │ ├── entrypoint.sh │ │ └── userdb.sql └── src │ ├── User.API │ ├── Controllers │ │ ├── BaseController.cs │ │ └── UserController.cs │ ├── Dockerfile │ ├── Filters │ │ └── ApiExceptionFilterAttribute.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Startup.cs │ ├── User.API.csproj │ ├── appsettings.Development.json │ └── appsettings.json │ ├── User.Application │ ├── Common │ │ ├── BaseClass │ │ │ └── ApplicationBase.cs │ │ ├── Behaviors │ │ │ ├── LoggingBehaviour.cs │ │ │ ├── PerformanceBehaviour.cs │ │ │ ├── UnhandledExceptionBehaviour.cs │ │ │ └── ValidationBehaviour.cs │ │ ├── Exceptions │ │ │ ├── NotFoundException.cs │ │ │ └── ValidationException.cs │ │ ├── Interfaces │ │ │ └── IConfigConstants.cs │ │ └── Mappings │ │ │ ├── IMapFrom.cs │ │ │ └── MappingProfile.cs │ ├── DependencyInjection.cs │ ├── User.Application.csproj │ └── User │ │ ├── Commands │ │ ├── AddUserCommand.cs │ │ ├── AddUserCommandValidator.cs │ │ ├── DeleteUserCommand.cs │ │ ├── DeleteUserCommandValidator.cs │ │ ├── UpdateUserCommand.cs │ │ └── UpdateUserCommandValidator.cs │ │ ├── DTO │ │ └── UserDTO.cs │ │ ├── Queries │ │ ├── GetAllUserQuery.cs │ │ ├── GetSingleUserQuery.cs │ │ └── GetSingleUserQueryValidator.cs │ │ └── VM │ │ └── UserVM.cs │ ├── User.Domain │ ├── Entities │ │ └── User.cs │ ├── Repositories │ │ └── IUserRepository.cs │ ├── UnitOfWork │ │ └── IUnitOfWork.cs │ └── User.Domain.csproj │ └── User.Persistence │ ├── Constant │ └── ConfigConstants.cs │ ├── DependencyInjection.cs │ ├── Repositories │ ├── Repository.cs │ └── UserRepository.cs │ ├── UnitOfWork │ └── UnitOfWork.cs │ └── User.Persistence.csproj ├── docker-compose.dcproj ├── docker-compose.override.yml └── docker-compose.yml /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /APIGateway/CMAPIGateway/CMAPIGateway.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Linux 6 | ..\.. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /APIGateway/CMAPIGateway/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 8 | WORKDIR /src 9 | COPY ["APIGateway/CMAPIGateway/CMAPIGateway.csproj", "APIGateway/CMAPIGateway/"] 10 | RUN dotnet restore "APIGateway/CMAPIGateway/CMAPIGateway.csproj" 11 | COPY . . 12 | WORKDIR "/src/APIGateway/CMAPIGateway" 13 | RUN dotnet build "CMAPIGateway.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CMAPIGateway.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CMAPIGateway.dll"] -------------------------------------------------------------------------------- /APIGateway/CMAPIGateway/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace CMAPIGateway 6 | { 7 | public class Program 8 | { 9 | public static void Main(string[] args) 10 | { 11 | CreateHostBuilder(args).Build().Run(); 12 | } 13 | 14 | public static IHostBuilder CreateHostBuilder(string[] args) => 15 | Host.CreateDefaultBuilder(args) 16 | .ConfigureWebHostDefaults(webBuilder => 17 | { 18 | webBuilder.UseStartup(); 19 | }) 20 | .ConfigureAppConfiguration((hostingContext, config) => 21 | { 22 | config.AddJsonFile("ocelot.json"); 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /APIGateway/CMAPIGateway/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:39048", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "CMAPIGateway": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "weatherforecast", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5000" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/weatherforecast", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /APIGateway/CMAPIGateway/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /APIGateway/CMAPIGateway/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Course/db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/mssql/server:2017-CU24-ubuntu-16.04 2 | COPY ./Course/db/data / 3 | RUN chmod +x ./SqlCmdStartup.sh 4 | CMD /bin/bash ./entrypoint.sh -------------------------------------------------------------------------------- /Course/db/data/SqlCmdStartup.sh: -------------------------------------------------------------------------------- 1 | sleep 15 2 | /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Yasser_123 -d master -i coursedb.sql -------------------------------------------------------------------------------- /Course/db/data/coursedb.sql: -------------------------------------------------------------------------------- 1 | USE [master] 2 | GO 3 | /****** Object: Database [CourseManagement] Script Date: 2/4/2021 11:58:40 PM ******/ 4 | CREATE DATABASE [CourseManagement] 5 | GO 6 | USE [CourseManagement] 7 | /****** Object: Table [dbo].[Course] Script Date: 2/4/2021 11:58:40 PM ******/ 8 | CREATE TABLE [dbo].[Course]( 9 | [CourseID] [int] IDENTITY(1,1) NOT NULL, 10 | [CourseName] [varchar](500) NOT NULL, 11 | [CourseShortName] [varchar](20) NULL, 12 | [CreditHour] [decimal](18, 2) NOT NULL, 13 | [Price] [decimal](18, 2) NOT NULL, 14 | [DateAdded] [datetime] NULL, 15 | [DateUpdated] [datetime] NULL, 16 | CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED 17 | ( 18 | [CourseID] ASC 19 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 20 | ) ON [PRIMARY] 21 | GO 22 | USE [master] 23 | GO 24 | ALTER DATABASE [CourseManagement] SET READ_WRITE 25 | GO 26 | -------------------------------------------------------------------------------- /Course/db/data/entrypoint.sh: -------------------------------------------------------------------------------- 1 | ./SqlCmdStartup.sh & /opt/mssql/bin/sqlservr -------------------------------------------------------------------------------- /Course/src/Course.API/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | namespace Course.API.Controllers 2 | { 3 | using MediatR; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | [Route("api/[controller]")] 8 | [ApiController] 9 | public class BaseController : ControllerBase 10 | { 11 | private IMediator mediator; 12 | 13 | /// 14 | /// Gets the Mediator. 15 | /// 16 | protected IMediator Mediator => this.mediator ??= this.HttpContext.RequestServices.GetService(); 17 | } 18 | } -------------------------------------------------------------------------------- /Course/src/Course.API/Controllers/CourseController.cs: -------------------------------------------------------------------------------- 1 | namespace Course.API.Controllers 2 | { 3 | using System.Threading.Tasks; 4 | using Course.Application.Course.VM; 5 | using Course.Application.User.Commands; 6 | using Course.Application.User.Queries; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | [Route("api/[controller]")] 10 | [ApiController] 11 | public class CourseController : BaseController 12 | { 13 | 14 | [HttpGet("{id}")] 15 | public async Task> Get(int id) 16 | { 17 | return await this.Mediator.Send(new GetSingleCourseQuery { CourseID = (int)id }); 18 | } 19 | 20 | [HttpGet] 21 | public async Task> Get() 22 | { 23 | return await this.Mediator.Send(new GetAllCourseQuery()); 24 | } 25 | 26 | [HttpPost] 27 | public async Task> Post(AddCourseCommand command) 28 | { 29 | return await this.Mediator.Send(command); 30 | } 31 | 32 | [HttpPut] 33 | public async Task> Put(UpdateCourseCommand command) 34 | { 35 | return await this.Mediator.Send(command); 36 | } 37 | 38 | [HttpDelete("{id}")] 39 | public async Task> Delete(int id) 40 | { 41 | return await this.Mediator.Send(new DeleteCourseCommand { CourseID = id }); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Course/src/Course.API/Course.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Linux 6 | ..\..\.. 7 | ..\..\docker-compose.dcproj 8 | 38f15097-9e62-49df-8efc-86e39cab3325 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Course/src/Course.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 8 | WORKDIR /src 9 | COPY ["Course/Course.API/Course.API.csproj", "Course/Course.API/"] 10 | RUN dotnet restore "Course/Course.API/Course.API.csproj" 11 | COPY . . 12 | WORKDIR "/src/Course/Course.API" 13 | RUN dotnet build "Course.API.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "Course.API.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "Course.API.dll"] -------------------------------------------------------------------------------- /Course/src/Course.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Course.API 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Course/src/Course.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:58634", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "Course.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5000" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Course/src/Course.API/Startup.cs: -------------------------------------------------------------------------------- 1 | using Course.Application; 2 | using Course.Persistence; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.OpenApi.Models; 9 | 10 | namespace Course.API 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddApplication(); 25 | services.AddPersistance(); 26 | services.AddControllers(); 27 | services.AddSwaggerGen(c => 28 | { 29 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Course.API", Version = "v1" }); 30 | }); 31 | } 32 | 33 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 34 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 35 | { 36 | if (env.IsDevelopment()) 37 | { 38 | app.UseDeveloperExceptionPage(); 39 | app.UseSwagger(); 40 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Course.API v1")); 41 | } 42 | 43 | app.UseRouting(); 44 | app.UseAuthorization(); 45 | 46 | app.UseEndpoints(endpoints => 47 | { 48 | endpoints.MapControllers(); 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Course/src/Course.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Course/src/Course.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "CourseConnection": "Server=coursedb;Database=CourseManagement;User Id=sa;Password=Yasser_123;" 4 | }, 5 | "AppSettings": { 6 | "LongRunningProcessMilliseconds": "1500", 7 | "MSG_COURSE_COURSENAME": "Course Name is required!", 8 | "MSG_COURSE_NULLCREDITHOUR": "Credit Hour is required!", 9 | "MSG_COURSE_COURSEID": "Course ID is required!" 10 | }, 11 | "Logging": { 12 | "LogLevel": { 13 | "Default": "Information", 14 | "Microsoft": "Warning", 15 | "Microsoft.Hosting.Lifetime": "Information" 16 | } 17 | }, 18 | "AllowedHosts": "*" 19 | } -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/BaseClass/ApplicationBase.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.BaseClass 2 | { 3 | using AutoMapper; 4 | using global::Course.Application.Common.Interfaces; 5 | using global::Course.Domain.UnitOfWork; 6 | 7 | public class ApplicationBase 8 | { 9 | public IUnitOfWork UnitOfWork { get; set; } 10 | public IConfigConstants ConfigConstants { get; set; } 11 | public IMapper Mapper { get; set; } 12 | 13 | public ApplicationBase(IConfigConstants configConstants, IUnitOfWork unitOfWork, IMapper mapper) 14 | { 15 | ConfigConstants = configConstants; 16 | UnitOfWork = unitOfWork; 17 | Mapper = mapper; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Behaviors/LoggingBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Behaviors 2 | { 3 | using MediatR.Pipeline; 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | public class LoggingBehaviour : IRequestPreProcessor 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public LoggingBehaviour(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public async Task Process(TRequest request, CancellationToken cancellationToken) 18 | { 19 | var requestName = typeof(TRequest).Name; 20 | string userName = string.Empty; 21 | 22 | await Task.Run(() => _logger.LogInformation("UserManagement Request: {Name} {@UserName} {@Request}", 23 | requestName, userName, request)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Behaviors/PerformanceBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Behaviors 2 | { 3 | using global::Course.Application.Common.Interfaces; 4 | using MediatR; 5 | using Microsoft.Extensions.Logging; 6 | using System.Diagnostics; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | public class PerformanceBehaviour : IPipelineBehavior 11 | { 12 | private readonly Stopwatch _timer; 13 | private readonly ILogger _logger; 14 | private readonly IConfigConstants _constact; 15 | 16 | public PerformanceBehaviour(ILogger logger, IConfigConstants constants) 17 | { 18 | _timer = new Stopwatch(); 19 | _logger = logger; 20 | _constact = constants; 21 | } 22 | 23 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 24 | { 25 | _timer.Start(); 26 | 27 | var response = await next(); 28 | 29 | _timer.Stop(); 30 | 31 | var elapsedMilliseconds = _timer.ElapsedMilliseconds; 32 | 33 | if (elapsedMilliseconds > _constact.LongRunningProcessMilliseconds) 34 | { 35 | var requestName = typeof(TRequest).Name; 36 | var userName = string.Empty; 37 | _logger.LogWarning("UserManagement Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {@UserName} {@Request}", 38 | requestName, elapsedMilliseconds, userName, request); 39 | } 40 | 41 | return response; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Behaviors/UnhandledExceptionBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Behaviours 2 | { 3 | using MediatR; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | public class UnhandledExceptionBehaviour : IPipelineBehavior 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public UnhandledExceptionBehaviour(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 18 | { 19 | try 20 | { 21 | return await next(); 22 | } 23 | catch (Exception ex) 24 | { 25 | var requestName = typeof(TRequest).Name; 26 | 27 | _logger.LogError(ex, "UserManagement Request: Unhandled Exception for Request {Name} {@Request}", requestName, request); 28 | 29 | throw; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Behaviors/ValidationBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Behaviours 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using FluentValidation; 8 | using MediatR; 9 | using ValidationException = Exceptions.ValidationException; 10 | public class ValidationBehaviour : IPipelineBehavior 11 | where TRequest : IRequest 12 | { 13 | private readonly IEnumerable> _validators; 14 | 15 | public ValidationBehaviour(IEnumerable> validators) 16 | { 17 | _validators = validators; 18 | } 19 | 20 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 21 | { 22 | if (_validators.Any()) 23 | { 24 | var context = new ValidationContext(request); 25 | 26 | var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken))); 27 | var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); 28 | 29 | if (failures.Count != 0) 30 | throw new ValidationException(failures); 31 | } 32 | return await next(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Exceptions 2 | { 3 | using System; 4 | public class NotFoundException : Exception 5 | { 6 | public NotFoundException() 7 | : base() 8 | { 9 | } 10 | 11 | public NotFoundException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public NotFoundException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | 21 | public NotFoundException(string name, object key) 22 | : base($"Entity \"{name}\" ({key}) was not found.") 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Exceptions/ValidationException.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Exceptions 2 | { 3 | using FluentValidation.Results; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | public class ValidationException : Exception 8 | { 9 | public ValidationException() 10 | : base("One or more validation failures have occurred.") 11 | { 12 | Errors = new Dictionary(); 13 | } 14 | 15 | public ValidationException(IEnumerable failures) 16 | : this() 17 | { 18 | Errors = failures 19 | .GroupBy(e => e.PropertyName, e => e.ErrorMessage) 20 | .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); 21 | } 22 | 23 | public IDictionary Errors { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Interfaces/IConfigConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Interfaces 2 | { 3 | public interface IConfigConstants 4 | { 5 | string CourseConnection { get; } 6 | string TestFullStackConnection { get; } 7 | int LongRunningProcessMilliseconds { get; } 8 | string MSG_COURSE_COURSENAME { get; } 9 | string MSG_COURSE_NULLCREDITHOUR { get; } 10 | string MSG_COURSE_COURSEID { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Mappings/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Mappings 2 | { 3 | using AutoMapper; 4 | public interface IMapFrom 5 | { 6 | void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType()).ReverseMap(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Common/Mappings/MappingProfile.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Common.Mappings 2 | { 3 | using AutoMapper; 4 | using System; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | public class MappingProfile : Profile 9 | { 10 | public MappingProfile() 11 | { 12 | ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly()); 13 | } 14 | 15 | private void ApplyMappingsFromAssembly(Assembly assembly) 16 | { 17 | var types = assembly.GetExportedTypes() 18 | .Where(t => t.GetInterfaces().Any(i => 19 | i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>))) 20 | .ToList(); 21 | 22 | foreach (var type in types) 23 | { 24 | var instance = Activator.CreateInstance(type); 25 | 26 | var methodInfo = type.GetMethod("Mapping") 27 | ?? type.GetInterface("IMapFrom`1").GetMethod("Mapping"); 28 | 29 | methodInfo?.Invoke(instance, new object[] { this }); 30 | 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Commands/AddCourseCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using AutoMapper; 4 | using MediatR; 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using global::Course.Application.Common.BaseClass; 9 | using global::Course.Application.Common.Interfaces; 10 | using global::Course.Domain.UnitOfWork; 11 | using global::Course.Domain.Entities; 12 | public class AddCourseCommand : IRequest 13 | { 14 | public string CourseName { get; set; } 15 | public string CourseShortName { get; set; } 16 | public double CreditHour { get; set; } 17 | public double Price { get; set; } 18 | public class AddNewUserHandler : ApplicationBase, IRequestHandler 19 | { 20 | public AddNewUserHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 21 | : base(constant, unitOfWork, mapper) 22 | { 23 | } 24 | 25 | public async Task Handle(AddCourseCommand request, CancellationToken cancellationToken) 26 | { 27 | var user = new Course 28 | { 29 | CourseName = request.CourseName, 30 | CourseShortName = request.CourseShortName, 31 | CreditHour = request.CreditHour, 32 | Price = request.Price, 33 | }; 34 | this.UnitOfWork.StartTransaction(); 35 | var res = UnitOfWork.Users.AddCourse(user).Result; 36 | this.UnitOfWork.Commit(); 37 | return await Task.Run(() => res, cancellationToken); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Commands/AddCourseCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using FluentValidation; 4 | using System.Linq; 5 | using global::Course.Application.Common.Interfaces; 6 | public class AddCourseCommandValidator : AbstractValidator 7 | { 8 | public AddCourseCommandValidator(IConfigConstants constant) 9 | { 10 | this.RuleFor(v => v.CourseName).NotEmpty().WithMessage(constant.MSG_COURSE_COURSENAME); 11 | this.RuleFor(v => v.CreditHour).GreaterThan(0).WithMessage(constant.MSG_COURSE_NULLCREDITHOUR); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Commands/DeleteCourseCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using AutoMapper; 4 | using MediatR; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using global::Course.Application.Common.BaseClass; 8 | using global::Course.Application.Common.Interfaces; 9 | using global::Course.Domain.UnitOfWork; 10 | public class DeleteCourseCommand : IRequest 11 | { 12 | public int CourseID { get; set; } 13 | public class DeleteUserHandler : ApplicationBase, IRequestHandler 14 | { 15 | public DeleteUserHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 16 | : base(constant, unitOfWork, mapper) 17 | { 18 | } 19 | 20 | public async Task Handle(DeleteCourseCommand request, CancellationToken cancellationToken) 21 | { 22 | this.UnitOfWork.StartTransaction(); 23 | var res = UnitOfWork.Users.DeleteCourse(request.CourseID).Result; 24 | this.UnitOfWork.Commit(); 25 | return await Task.Run(() => res, cancellationToken); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Commands/DeleteCourseCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using FluentValidation; 4 | using global::Course.Application.Common.Interfaces; 5 | public class DeleteCourseCommandValidator : AbstractValidator 6 | { 7 | public DeleteCourseCommandValidator(IConfigConstants constant) 8 | { 9 | this.RuleFor(v => v.CourseID).GreaterThan(0).WithMessage(constant.MSG_COURSE_COURSEID); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Commands/UpdateCourseCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using FluentValidation; 4 | using global::Course.Application.Common.Interfaces; 5 | public class UpdateCourseCommandValidator : AbstractValidator 6 | { 7 | public UpdateCourseCommandValidator(IConfigConstants constant) 8 | { 9 | this.RuleFor(v => v.CourseName).NotEmpty().WithMessage(constant.MSG_COURSE_COURSENAME); 10 | this.RuleFor(v => v.CreditHour).GreaterThan(0).WithMessage(constant.MSG_COURSE_NULLCREDITHOUR); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/DTO/CourseDTO.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Course.DTO 2 | { 3 | using AutoMapper; 4 | using System; 5 | using global::Course.Domain.Entities; 6 | using global::Course.Application.Common.Mappings; 7 | 8 | public class CourseDTO : IMapFrom 9 | { 10 | public int CourseID { get; set; } 11 | public string CourseName { get; set; } 12 | public string CourseShortName { get; set; } 13 | public double CreditHour { get; set; } 14 | public double Price { get; set; } 15 | public DateTime DateAdded { get; set; } 16 | public DateTime? DateUpdated { get; set; } 17 | public void Mapping(Profile profile) 18 | { 19 | profile.CreateMap(); 20 | profile.CreateMap(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Queries/GetAllCourseQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Queries 2 | { 3 | using AutoMapper; 4 | using MediatR; 5 | using System.Collections.Generic; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using global::Course.Application.Common.BaseClass; 9 | using global::Course.Application.Common.Interfaces; 10 | using global::Course.Domain.UnitOfWork; 11 | using global::Course.Application.Course.VM; 12 | using global::Course.Application.Course.DTO; 13 | 14 | public class GetAllCourseQuery : IRequest 15 | { 16 | public class GetAllUserHandler : ApplicationBase, IRequestHandler 17 | { 18 | public GetAllUserHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 19 | : base(constant, unitOfWork, mapper) 20 | { 21 | } 22 | 23 | public async Task Handle(GetAllCourseQuery request, CancellationToken cancellationToken) 24 | { 25 | var res = Mapper.Map(UnitOfWork.Users.GetAllCourses().Result, new List()); 26 | return await Task.FromResult(new CourseVM() { CourseList = res }); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Queries/GetSingleCourseQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Queries 2 | { 3 | using AutoMapper; 4 | using MediatR; 5 | using System.Collections.Generic; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using global::Course.Application.Common.BaseClass; 9 | using global::Course.Application.Common.Interfaces; 10 | using global::Course.Domain.UnitOfWork; 11 | using global::Course.Application.Course.VM; 12 | using global::Course.Application.Course.DTO; 13 | 14 | public class GetSingleCourseQuery : IRequest 15 | { 16 | public int CourseID { get; set; } 17 | public class GetSingleUserHandler : ApplicationBase, IRequestHandler 18 | { 19 | public GetSingleUserHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 20 | : base(constant, unitOfWork, mapper) 21 | { 22 | } 23 | 24 | public async Task Handle(GetSingleCourseQuery request, CancellationToken cancellationToken) 25 | { 26 | var res = this.Mapper.Map(this.UnitOfWork.Users.GetCourse(request.CourseID).Result, new CourseDTO()); 27 | return await Task.FromResult(new CourseVM() { CourseList = new List { res } }); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/Queries/GetSingleCourseQueryValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Queries 2 | { 3 | using FluentValidation; 4 | using global::Course.Application.Common.Interfaces; 5 | public class GetSingleCourseQueryValidator : AbstractValidator 6 | { 7 | public GetSingleCourseQueryValidator(IConfigConstants constant) 8 | { 9 | this.RuleFor(v => v.CourseID).GreaterThan(0).WithMessage(constant.MSG_COURSE_COURSEID); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Course/src/Course.Application/Course/VM/CourseVM.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.Course.VM 2 | { 3 | using global::Course.Application.Course.DTO; 4 | using System.Collections.Generic; 5 | public class CourseVM 6 | { 7 | public IList CourseList { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Course/src/Course.Application/DependencyInjection.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application 2 | { 3 | using AutoMapper; 4 | using FluentValidation; 5 | using global::Course.Application.Common.Behaviors; 6 | using global::Course.Application.Common.Behaviours; 7 | using MediatR; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using System.Reflection; 10 | 11 | public static class DependencyInjection 12 | { 13 | public static IServiceCollection AddApplication(this IServiceCollection services) 14 | { 15 | services.AddAutoMapper(Assembly.GetExecutingAssembly()); 16 | services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); 17 | services.AddMediatR(Assembly.GetExecutingAssembly()); 18 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>)); 19 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)); 20 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>)); 21 | return services; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Course/src/Course.Domain/Course.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Course/src/Course.Domain/Entities/Course.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Domain.Entities 2 | { 3 | using Dapper.Contrib.Extensions; 4 | using System; 5 | 6 | [Table("[Course]")] 7 | public class Course 8 | { 9 | [Key] 10 | public int CourseID { get; set; } 11 | public string CourseName { get; set; } 12 | public string CourseShortName { get; set; } 13 | public double CreditHour { get; set; } 14 | public double Price { get; set; } 15 | public DateTime DateAdded { get; set; } 16 | public DateTime? DateUpdated { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Course/src/Course.Domain/Repositories/ICourseRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Domain.Repositories 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Course.Domain.Entities; 6 | 7 | public interface ICourseRepository 8 | { 9 | Task AddCourse(Course course); 10 | Task UpdateCourse(Course course); 11 | Task DeleteCourse(int courseId); 12 | Task> GetAllCourses(); 13 | Task GetCourse(long courseId); 14 | Task DeleteAllCourses(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Course/src/Course.Domain/UnitOfWork/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Domain.UnitOfWork 2 | { 3 | using Course.Domain.Repositories; 4 | public interface IUnitOfWork 5 | { 6 | ICourseRepository Users { get; } 7 | void StartTransaction(); 8 | void Commit(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Course/src/Course.Persistence/Constant/ConfigConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Persistence.Constant 2 | { 3 | using Course.Application.Common.Interfaces; 4 | using Microsoft.Extensions.Configuration; 5 | public class ConfigConstants : IConfigConstants 6 | { 7 | public IConfiguration Configuration { get; } 8 | public ConfigConstants(IConfiguration configuration) 9 | { 10 | this.Configuration = configuration; 11 | } 12 | public string CourseConnection => this.Configuration.GetConnectionString("CourseConnection"); 13 | 14 | public string TestFullStackConnection => this.Configuration.GetConnectionString("TestFullStackConnection"); 15 | public int LongRunningProcessMilliseconds => int.Parse(this.Configuration["AppSettings:LongRunningProcessMilliseconds"]); 16 | 17 | public string MSG_COURSE_COURSENAME => this.Configuration["AppSettings:MSG_COURSE_COURSENAME"]; 18 | 19 | public string MSG_COURSE_NULLCREDITHOUR => this.Configuration["AppSettings:MSG_COURSE_NULLCREDITHOUR"]; 20 | 21 | public string MSG_COURSE_COURSEID => this.Configuration["AppSettings:MSG_COURSE_COURSEID"]; 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Course/src/Course.Persistence/Course.Persistence.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Course/src/Course.Persistence/DependencyInjection.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Persistence 2 | { 3 | using Course.Application.Common.Interfaces; 4 | using Course.Domain.UnitOfWork; 5 | using Course.Persistence.Constant; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using System.Data; 8 | using System.Data.SqlClient; 9 | 10 | public static class DependencyInjection 11 | { 12 | public static IServiceCollection AddPersistance(this IServiceCollection services) 13 | { 14 | services.AddSingleton(); 15 | services.AddSingleton(conn => new SqlConnection(conn.GetService().CourseConnection)); 16 | services.AddTransient(uof => new UnitOfWork.UnitOfWork(uof.GetService())); 17 | return services; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Course/src/Course.Persistence/Repositories/CourseRepository.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Persistence.Repositories 2 | { 3 | using Course.Domain.Repositories; 4 | using Dapper.Contrib.Extensions; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Data; 8 | using System.Threading.Tasks; 9 | using CourseEntity = Course.Domain.Entities.Course; 10 | 11 | public class CourseRepository : Repository, ICourseRepository 12 | { 13 | public CourseRepository(IDbConnection dbConnection, IDbTransaction dbtransaction) 14 | : base(dbConnection, dbtransaction) 15 | { 16 | } 17 | public Task AddCourse(CourseEntity course) 18 | { 19 | course.DateAdded = DateTime.Now; 20 | course.DateUpdated = null; 21 | return DbConnection.InsertAsync(course, DbTransaction); 22 | } 23 | 24 | public Task DeleteCourse(int courseId) 25 | { 26 | return DbConnection.DeleteAsync(new CourseEntity { CourseID = courseId }, DbTransaction); 27 | } 28 | 29 | public Task> GetAllCourses() 30 | { 31 | return DbConnection.GetAllAsync(); 32 | } 33 | 34 | public Task GetCourse(long courseId) 35 | { 36 | return DbConnection.GetAsync(courseId); 37 | } 38 | 39 | public Task UpdateCourse(CourseEntity Course) 40 | { 41 | Course.DateUpdated = DateTime.Now; 42 | return DbConnection.UpdateAsync(Course, DbTransaction); 43 | } 44 | 45 | public Task DeleteAllCourses() 46 | { 47 | return DbConnection.DeleteAllAsync(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Course/src/Course.Persistence/Repositories/Repository.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Persistence.Repositories 2 | { 3 | using System.Data; 4 | public class Repository 5 | { 6 | protected IDbConnection DbConnection { get; } 7 | protected IDbTransaction DbTransaction { get; } 8 | public Repository(IDbConnection dbConnection, IDbTransaction dbTransaction) 9 | { 10 | DbTransaction = dbTransaction; 11 | DbConnection = dbConnection; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Course/src/Course.Persistence/UnitOfWork/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Persistence.UnitOfWork 2 | { 3 | using global::Course.Domain.Repositories; 4 | using global::Course.Domain.UnitOfWork; 5 | using global::Course.Persistence.Repositories; 6 | using System.Data; 7 | 8 | public class UnitOfWork : IUnitOfWork 9 | { 10 | private IDbConnection dbConnection; 11 | private IDbTransaction transaction; 12 | public UnitOfWork(IDbConnection dbConnection) 13 | { 14 | this.dbConnection = dbConnection; 15 | this.ManageConnection(); 16 | } 17 | public ICourseRepository Users => new CourseRepository(this.dbConnection, this.transaction); 18 | 19 | public void StartTransaction() 20 | { 21 | 22 | if (this.transaction == null) 23 | { 24 | this.transaction = this.dbConnection.BeginTransaction(); 25 | } 26 | } 27 | public void Commit() 28 | { 29 | try 30 | { 31 | this.transaction.Commit(); 32 | } 33 | catch 34 | { 35 | this.transaction.Rollback(); 36 | } 37 | } 38 | 39 | private void ManageConnection() 40 | { 41 | if (this.dbConnection.State == ConnectionState.Closed) 42 | { 43 | this.dbConnection.Open(); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | namespace CoursesBasket.API.Controllers 2 | { 3 | using MediatR; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | [Route("api/[controller]")] 8 | [ApiController] 9 | public class BaseController : ControllerBase 10 | { 11 | private IMediator mediator; 12 | 13 | /// 14 | /// Gets the Mediator. 15 | /// 16 | protected IMediator Mediator => this.mediator ??= this.HttpContext.RequestServices.GetService(); 17 | } 18 | } -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/Controllers/CoursesBasketController.cs: -------------------------------------------------------------------------------- 1 | using CourseBasket.Application.CoursesBasket.Commands; 2 | using CourseBasket.Application.CoursesBasket.DTO; 3 | using CourseBasket.Application.CoursesBasket.VM; 4 | using CourseBasket.Domain.Entities; 5 | using CoursesBasket.API.Controllers; 6 | using CoursesBasket.Application.CoursesBasket.Commands; 7 | using Microsoft.AspNetCore.Mvc; 8 | using System.Collections.Generic; 9 | using System.Threading.Tasks; 10 | 11 | namespace CourseBasket.API.Controllers 12 | { 13 | [Route("api/[controller]")] 14 | [ApiController] 15 | public class CoursesBasketController : BaseController 16 | { 17 | [HttpPost("[action]")] 18 | public async Task>> GetAll(List userNames) 19 | { 20 | return await this.Mediator.Send(new GetAllItemBasketQuery { UserNames = userNames }); 21 | } 22 | 23 | [HttpGet("{userName}")] 24 | public async Task> Get(string userName) 25 | { 26 | return await this.Mediator.Send(new GetBasketQuery { UserName = userName }); 27 | } 28 | 29 | [HttpPut] 30 | public async Task> Put(UpdateBasketCommand command) 31 | { 32 | return await this.Mediator.Send(command); 33 | } 34 | 35 | [HttpPost] 36 | public async Task> Post(List basketCheckout) 37 | { 38 | return await this.Mediator.Send(new CheckoutBasketCommand() { BasketCheckoutDTO = basketCheckout }); 39 | } 40 | 41 | [HttpDelete("{userName}")] 42 | public async Task> Delete(string userName) 43 | { 44 | return await this.Mediator.Send(new DeleteBasketCommand { UserName = userName }); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/CourseBasket.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Linux 6 | ..\..\.. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 8 | WORKDIR /src 9 | COPY ["CourseBasket/src/CourseBasket.API/CourseBasket.API.csproj", "CourseBasket/src/CourseBasket.API/"] 10 | RUN dotnet restore "CourseBasket/src/CourseBasket.API/CourseBasket.API.csproj" 11 | COPY . . 12 | WORKDIR "/src/CourseBasket/src/CourseBasket.API" 13 | RUN dotnet build "CourseBasket.API.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CourseBasket.API.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CourseBasket.API.dll"] -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace CourseBasket.API 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59522", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "CourseBasket.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5000" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "Redis": "basketdb:6379" 4 | }, 5 | "AppSettings": { 6 | "LongRunningProcessMilliseconds": "1500" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft": "Warning", 12 | "Microsoft.Hosting.Lifetime": "Information" 13 | } 14 | }, 15 | "AllowedHosts": "*", 16 | "EventBus": { 17 | "HostName": "rabbitmq", 18 | "UserName": "guest", 19 | "Password": "guest" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/BaseClass/ApplicationBase.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using CourseBasket.Application.Common.Interfaces; 3 | using CourseBasket.Domain.UnitOfWork; 4 | 5 | namespace CourseBasket.Application.Common.BaseClass 6 | { 7 | public class ApplicationBase 8 | { 9 | 10 | public IUnitOfWork UnitOfWork { get; set; } 11 | public IConfigConstants ConfigConstants { get; set; } 12 | public IMapper Mapper { get; set; } 13 | 14 | public ApplicationBase(IConfigConstants configConstants, IUnitOfWork unitOfWork, IMapper mapper) 15 | { 16 | ConfigConstants = configConstants; 17 | UnitOfWork = unitOfWork; 18 | Mapper = mapper; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Behaviors/LoggingBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Behaviors 2 | { 3 | using MediatR.Pipeline; 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | public class LoggingBehaviour : IRequestPreProcessor 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public LoggingBehaviour(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public async Task Process(TRequest request, CancellationToken cancellationToken) 18 | { 19 | var requestName = typeof(TRequest).Name; 20 | string userName = string.Empty; 21 | 22 | await Task.Run(() => _logger.LogInformation("UserManagement Request: {Name} {@UserName} {@Request}", 23 | requestName, userName, request)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Behaviors/PerformanceBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Behaviors 2 | { 3 | using global::CourseBasket.Application.Common.Interfaces; 4 | using MediatR; 5 | using Microsoft.Extensions.Logging; 6 | using System.Diagnostics; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | public class PerformanceBehaviour : IPipelineBehavior 11 | { 12 | private readonly Stopwatch _timer; 13 | private readonly ILogger _logger; 14 | private readonly IConfigConstants _constact; 15 | 16 | public PerformanceBehaviour(ILogger logger, IConfigConstants constants) 17 | { 18 | _timer = new Stopwatch(); 19 | _logger = logger; 20 | _constact = constants; 21 | } 22 | 23 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 24 | { 25 | _timer.Start(); 26 | 27 | var response = await next(); 28 | 29 | _timer.Stop(); 30 | 31 | var elapsedMilliseconds = _timer.ElapsedMilliseconds; 32 | 33 | if (elapsedMilliseconds > _constact.LongRunningProcessMilliseconds) 34 | { 35 | var requestName = typeof(TRequest).Name; 36 | var userName = string.Empty; 37 | _logger.LogWarning("UserManagement Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {@UserName} {@Request}", 38 | requestName, elapsedMilliseconds, userName, request); 39 | } 40 | 41 | return response; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Behaviors/UnhandledExceptionBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Behaviours 2 | { 3 | using MediatR; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | public class UnhandledExceptionBehaviour : IPipelineBehavior 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public UnhandledExceptionBehaviour(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 18 | { 19 | try 20 | { 21 | return await next(); 22 | } 23 | catch (Exception ex) 24 | { 25 | var requestName = typeof(TRequest).Name; 26 | 27 | _logger.LogError(ex, "UserManagement Request: Unhandled Exception for Request {Name} {@Request}", requestName, request); 28 | 29 | throw; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Behaviors/ValidationBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Behaviours 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using FluentValidation; 8 | using MediatR; 9 | using ValidationException = Exceptions.ValidationException; 10 | public class ValidationBehaviour : IPipelineBehavior 11 | where TRequest : IRequest 12 | { 13 | private readonly IEnumerable> _validators; 14 | 15 | public ValidationBehaviour(IEnumerable> validators) 16 | { 17 | _validators = validators; 18 | } 19 | 20 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 21 | { 22 | if (_validators.Any()) 23 | { 24 | var context = new ValidationContext(request); 25 | 26 | var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken))); 27 | var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); 28 | 29 | if (failures.Count != 0) 30 | throw new ValidationException(failures); 31 | } 32 | return await next(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Exceptions/BadRequestException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CourseBasket.Application.Common.Exceptions 5 | { 6 | public class BadRequestException : Exception 7 | { 8 | public BadRequestException() 9 | : base() 10 | { 11 | } 12 | 13 | public BadRequestException(string message) 14 | : base(message) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Exceptions 2 | { 3 | using System; 4 | public class NotFoundException : Exception 5 | { 6 | public NotFoundException() 7 | : base() 8 | { 9 | } 10 | 11 | public NotFoundException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public NotFoundException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | 21 | public NotFoundException(string name, object key) 22 | : base($"Entity \"{name}\" ({key}) was not found.") 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Exceptions/ValidationException.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Exceptions 2 | { 3 | using FluentValidation.Results; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | public class ValidationException : Exception 8 | { 9 | public ValidationException() 10 | : base("One or more validation failures have occurred.") 11 | { 12 | Errors = new Dictionary(); 13 | } 14 | 15 | public ValidationException(IEnumerable failures) 16 | : this() 17 | { 18 | Errors = failures 19 | .GroupBy(e => e.PropertyName, e => e.ErrorMessage) 20 | .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); 21 | } 22 | 23 | public IDictionary Errors { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Interfaces/IConfigConstants.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Interfaces 2 | { 3 | public interface IConfigConstants 4 | { 5 | string RedisConnection { get; } 6 | int LongRunningProcessMilliseconds { get; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Mappings/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.Common.Mappings 2 | { 3 | using AutoMapper; 4 | public interface IMapFrom 5 | { 6 | void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType()).ReverseMap(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/Common/Mappings/MappingProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using System; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace CourseBasket.Application.Common.Mappings 7 | { 8 | public class MappingProfile : Profile 9 | { 10 | public MappingProfile() 11 | { 12 | ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly()); 13 | } 14 | 15 | private void ApplyMappingsFromAssembly(Assembly assembly) 16 | { 17 | var types = assembly.GetExportedTypes() 18 | .Where(t => t.GetInterfaces().Any(i => 19 | i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>))) 20 | .ToList(); 21 | 22 | foreach (var type in types) 23 | { 24 | var instance = Activator.CreateInstance(type); 25 | 26 | var methodInfo = type.GetMethod("Mapping") 27 | ?? type.GetInterface("IMapFrom`1").GetMethod("Mapping"); 28 | 29 | methodInfo?.Invoke(instance, new object[] { this }); 30 | 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/CourseBasket.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/CoursesBasket/Commands/DeleteBasketCommand.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using CourseBasket.Application.Common.BaseClass; 3 | using CourseBasket.Application.Common.Interfaces; 4 | using CourseBasket.Domain.Entities; 5 | using CourseBasket.Domain.UnitOfWork; 6 | using MediatR; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace CoursesBasket.Application.CoursesBasket.Commands 11 | { 12 | public class DeleteBasketCommand : IRequest 13 | { 14 | public string UserName { get; set; } 15 | public class DeleteBasketHandler : ApplicationBase, IRequestHandler 16 | { 17 | public DeleteBasketHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 18 | : base(constant, unitOfWork, mapper) 19 | { 20 | } 21 | 22 | public async Task Handle(DeleteBasketCommand request, CancellationToken cancellationToken) 23 | { 24 | return await this.UnitOfWork.Basket.DeleteBasket(request.UserName); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/CoursesBasket/Commands/UpdateBasketCommand.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using CourseBasket.Application.Common.BaseClass; 3 | using CourseBasket.Application.Common.Interfaces; 4 | using CourseBasket.Domain.Entities; 5 | using CourseBasket.Domain.UnitOfWork; 6 | using MediatR; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace CourseBasket.Application.CoursesBasket.Commands 13 | { 14 | public class UpdateBasketCommand : IRequest 15 | { 16 | public string UserEmail { get; set; } 17 | public List Items { get; set; } 18 | 19 | public class UpdateBasketHandler : ApplicationBase, IRequestHandler 20 | { 21 | private BasketCart basket; 22 | public UpdateBasketHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 23 | : base(constant, unitOfWork, mapper) 24 | { 25 | } 26 | 27 | public async Task Handle(UpdateBasketCommand request, CancellationToken cancellationToken) 28 | { 29 | basket = new BasketCart { UserEmail = request.UserEmail, Items = request.Items.Distinct().ToList() }; 30 | return await this.UnitOfWork.Basket.UpdateBasket(basket); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/CoursesBasket/DTO/BasketCheckoutDTO.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.CoursesBasket.DTO 2 | { 3 | using AutoMapper; 4 | using CourseBasket.Application.Common.Mappings; 5 | using CourseBasket.Domain.Entities; 6 | using EventBusRabbitMQ.Events; 7 | 8 | public class BasketCheckoutDTO : IMapFrom, IMapFrom 9 | { 10 | public string Subjects { get; set; } 11 | public decimal TotalPrice { get; set; } 12 | public string FirstName { get; set; } 13 | public string LastName { get; set; } 14 | public string EmailAddress { get; set; } 15 | public string PhoneNumber { get; set; } 16 | public string Address { get; set; } 17 | 18 | public void Mapping(Profile profile) 19 | { 20 | profile.CreateMap(); 21 | profile.CreateMap(); 22 | 23 | profile.CreateMap(); 24 | profile.CreateMap(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/CoursesBasket/Queries/GetAllItemBasketQuery.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using CourseBasket.Application.Common.BaseClass; 3 | using CourseBasket.Application.Common.Interfaces; 4 | using CourseBasket.Application.CoursesBasket.VM; 5 | using CourseBasket.Domain.Entities; 6 | using CourseBasket.Domain.UnitOfWork; 7 | using MediatR; 8 | using System.Collections.Generic; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace CourseBasket.Application.CoursesBasket.Commands 13 | { 14 | public class GetAllItemBasketQuery : IRequest> 15 | { 16 | public List UserNames { get; set; } 17 | public class GetAllItemBasketHandler : ApplicationBase, IRequestHandler> 18 | { 19 | public GetAllItemBasketHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 20 | : base(constant, unitOfWork, mapper) 21 | { 22 | } 23 | 24 | public async Task> Handle(GetAllItemBasketQuery request, CancellationToken cancellationToken) 25 | { 26 | var tt = this.UnitOfWork.Basket.GetAllItemBasket(request.UserNames).Result; 27 | var res = Mapper.Map(this.UnitOfWork.Basket.GetAllItemBasket(request.UserNames).Result, new List()); 28 | return await Task.FromResult(res); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/CoursesBasket/Queries/GetBasketQuery.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using CourseBasket.Application.Common.BaseClass; 3 | using CourseBasket.Application.Common.Interfaces; 4 | using CourseBasket.Domain.Entities; 5 | using CourseBasket.Domain.UnitOfWork; 6 | using MediatR; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace CourseBasket.Application.CoursesBasket.Commands 11 | { 12 | public class GetBasketQuery : IRequest 13 | { 14 | public string UserName { get; set; } 15 | public class GetBasketHandler : ApplicationBase, IRequestHandler 16 | { 17 | public GetBasketHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 18 | : base(constant, unitOfWork, mapper) 19 | { 20 | } 21 | 22 | public async Task Handle(GetBasketQuery request, CancellationToken cancellationToken) 23 | { 24 | return await this.UnitOfWork.Basket.GetBasket(request.UserName); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Application/CoursesBasket/VM/CourseBasketVM.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Application.CoursesBasket.VM 2 | { 3 | using AutoMapper; 4 | using CourseBasket.Application.Common.Mappings; 5 | using CourseBasket.Domain.Entities; 6 | using System.Linq; 7 | 8 | public class CourseBasketVM : IMapFrom 9 | { 10 | public string UserEmail { get; set; } 11 | public string Subjects { get; set; } 12 | public decimal TotalPrice { get; set; } 13 | 14 | public void Mapping(Profile profile) 15 | { 16 | profile.CreateMap() 17 | .ForMember(d => d.Subjects, e => e.MapFrom(s => string.Join(",", s.Items.Select(x => x.CourseName).ToArray()))); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Domain/CourseBasket.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Domain/Entities/BasketCart.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CourseBasket.Domain.Entities 4 | { 5 | public class BasketCart 6 | { 7 | public string UserEmail { get; set; } 8 | public List Items { get; set; } = new List(); 9 | public decimal TotalPrice 10 | { 11 | get 12 | { 13 | decimal totalprice = 0; 14 | foreach (var item in Items) 15 | { 16 | totalprice += item.Price; 17 | } 18 | return totalprice; 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Domain/Entities/BasketCartItem.cs: -------------------------------------------------------------------------------- 1 | namespace CourseBasket.Domain.Entities 2 | { 3 | public class BasketCartItem 4 | { 5 | public int CourseID { get; set; } 6 | public string CourseName { get; set; } 7 | public string CourseShortName { get; set; } 8 | public decimal CreditHour { get; set; } 9 | public decimal Price { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Domain/Entities/BasketCheckout.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CourseBasket.Domain.Entities 4 | { 5 | public class BasketCheckout 6 | { 7 | public string Subjects { get; set; } 8 | public decimal TotalPrice { get; set; } 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | public string EmailAddress { get; set; } 12 | public string PhoneNumber { get; set; } 13 | public string Address { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Domain/Repositories/IBasketRepository.cs: -------------------------------------------------------------------------------- 1 | using CourseBasket.Domain.Entities; 2 | using EventBusRabbitMQ.Events; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace CourseBasket.Domain.Repositories 7 | { 8 | public interface IBasketRepository 9 | { 10 | Task> GetAllItemBasket(List userNames); 11 | Task GetBasket(string userName); 12 | Task UpdateBasket(BasketCart basket); 13 | Task DeleteBasket(string userName); 14 | 15 | Task CheckoutBasket(List coursesCheckoutEvent); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Domain/UnitOfWork/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using CourseBasket.Domain.Repositories; 2 | 3 | namespace CourseBasket.Domain.UnitOfWork 4 | { 5 | public interface IUnitOfWork 6 | { 7 | IBasketRepository Basket { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Persistence/Constant/ConfigConstants.cs: -------------------------------------------------------------------------------- 1 | using CourseBasket.Application.Common.Interfaces; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace CourseBasket.Persistence.Constant 5 | { 6 | public class ConfigConstants : IConfigConstants 7 | { 8 | public IConfiguration Configuration { get; } 9 | public ConfigConstants(IConfiguration configuration) 10 | { 11 | this.Configuration = configuration; 12 | } 13 | public string RedisConnection => this.Configuration.GetConnectionString("Redis"); 14 | public int LongRunningProcessMilliseconds => int.Parse(this.Configuration["AppSettings:LongRunningProcessMilliseconds"]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Persistence/CourseBasket.Persistence.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Persistence/DependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using CourseBasket.Application.Common.Interfaces; 2 | using CourseBasket.Domain.UnitOfWork; 3 | using CourseBasket.Persistence.Constant; 4 | using EventBusRabbitMQ.Producer; 5 | using EventBusRabbitMQ.RabbitMQConnection; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using RabbitMQ.Client; 9 | using StackExchange.Redis; 10 | 11 | namespace CourseBasket.Persistence 12 | { 13 | public static class DependencyInjection 14 | { 15 | public static IServiceCollection AddPersistance(this IServiceCollection services, IConfiguration configuration) 16 | { 17 | services.AddSingleton(); 18 | services.AddTransient(uof => new UnitOfWork.UnitOfWork(ConnectionMultiplexer.Connect(ConfigurationOptions.Parse(uof.GetService().RedisConnection, true)).GetDatabase())); 19 | services.AddSingleton(sp => 20 | { 21 | var factory = new ConnectionFactory() 22 | { 23 | HostName = configuration["EventBus:HostName"] 24 | }; 25 | 26 | if (!string.IsNullOrEmpty(configuration["EventBus:UserName"])) 27 | { 28 | factory.UserName = configuration["EventBus:UserName"]; 29 | } 30 | 31 | if (!string.IsNullOrEmpty(configuration["EventBus:Password"])) 32 | { 33 | factory.Password = configuration["EventBus:Password"]; 34 | } 35 | 36 | return new RabbitMQConnection(factory); 37 | }); 38 | 39 | services.AddSingleton(); 40 | return services; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /CourseBasket/src/CourseBasket.Persistence/UnitOfWork/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using CourseBasket.Domain.Repositories; 2 | using CourseBasket.Domain.UnitOfWork; 3 | using CourseBasket.Persistence.Repositories; 4 | using EventBusRabbitMQ.Producer; 5 | using StackExchange.Redis; 6 | 7 | namespace CourseBasket.Persistence.UnitOfWork 8 | { 9 | public class UnitOfWork : IUnitOfWork 10 | { 11 | public UnitOfWork(IDatabase redisDB) 12 | { 13 | this.RedisDB = redisDB; 14 | } 15 | 16 | public IDatabase RedisDB { get; } 17 | public EventBusRabbitMQProducer EventBusRabbitMQProducer { get; } 18 | public IBasketRepository Basket => new BasketRepository(RedisDB, EventBusRabbitMQProducer); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Student Course Management 2 | 3 | Student Course Management is a dockerized microservice solution developed in ASP.NET Core Web API. The front end is implemented in Angular 11. This project is not production-ready 4 | and developed for educational purposes only. 5 | 6 | ## Software/Framework Required to Set up Solution 7 | 8 | 1. Visual Studio 2019 with .NET 5 Framework 9 | 2. Angular 11+ 10 | 3. Docker Desktop 11 | 12 | ## Steps to Set up the Solution 13 | 14 | 1. Clone the master repository to a local drive, build and run the solution. 15 | 2. Front App URL: http://localhost:150 16 | 17 | ## View the Brief Article 18 | 19 | https://fullstackhub.io/asp-net-core-microservices-with-angular11/ 20 | -------------------------------------------------------------------------------- /RegistrationQueue/Common/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace EventBusRabbitMQ.Common 2 | { 3 | public static class Constants 4 | { 5 | public const string CourseCheckoutQueue = "CourseCheckoutQueue"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /RegistrationQueue/EventBusRabbitMQ.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RegistrationQueue/Events/CourseCheckoutEvent.cs: -------------------------------------------------------------------------------- 1 | namespace EventBusRabbitMQ.Events 2 | { 3 | public class CourseCheckoutEvent 4 | { 5 | public string Subjects { get; set; } 6 | public decimal TotalPrice { get; set; } 7 | public string FirstName { get; set; } 8 | public string LastName { get; set; } 9 | public string EmailAddress { get; set; } 10 | public string PhoneNumber { get; set; } 11 | public string Address { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /RegistrationQueue/Producer/EventBusRabbitMQProducer.cs: -------------------------------------------------------------------------------- 1 | using EventBusRabbitMQ.Events; 2 | using EventBusRabbitMQ.RabbitMQConnection; 3 | using Newtonsoft.Json; 4 | using RabbitMQ.Client; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace EventBusRabbitMQ.Producer 9 | { 10 | public class EventBusRabbitMQProducer 11 | { 12 | private readonly IRabbitMQConnection connection; 13 | 14 | public EventBusRabbitMQProducer(IRabbitMQConnection connection) 15 | { 16 | this.connection = connection; 17 | } 18 | 19 | public void PublishCoursesCheckout(string queueName, List publishModel) 20 | { 21 | using var channel = this.connection.CreateModel(); 22 | channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null); 23 | var message = JsonConvert.SerializeObject(publishModel); 24 | var body = Encoding.UTF8.GetBytes(message); 25 | 26 | IBasicProperties properties = channel.CreateBasicProperties(); 27 | properties.Persistent = true; 28 | properties.DeliveryMode = 2; 29 | 30 | channel.ConfirmSelect(); 31 | channel.BasicPublish(exchange: "", routingKey: queueName, mandatory: true, basicProperties: properties, body: body); 32 | channel.WaitForConfirmsOrDie(); 33 | 34 | channel.BasicAcks += (sender, eventArgs) => 35 | { 36 | System.Console.WriteLine("Sent RabbitMQ"); 37 | //implement ack handle 38 | }; 39 | channel.ConfirmSelect(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /RegistrationQueue/RabbitMQConnection/IRabbitMQConnection.cs: -------------------------------------------------------------------------------- 1 | using RabbitMQ.Client; 2 | using System; 3 | 4 | namespace EventBusRabbitMQ.RabbitMQConnection 5 | { 6 | public interface IRabbitMQConnection : IDisposable 7 | { 8 | bool IsConnected { get; } 9 | bool TryConnect(); 10 | IModel CreateModel(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SCM.Web/scm/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 18 | -------------------------------------------------------------------------------- /SCM.Web/scm/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/charts 16 | **/docker-compose* 17 | **/compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | README.md 25 | -------------------------------------------------------------------------------- /SCM.Web/scm/.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 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /SCM.Web/scm/.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 | -------------------------------------------------------------------------------- /SCM.Web/scm/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Docker Node.js Launch", 5 | "type": "docker", 6 | "request": "launch", 7 | "preLaunchTask": "docker-run: debug", 8 | "platform": "node" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /SCM.Web/scm/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "docker-build", 6 | "label": "docker-build", 7 | "platform": "node", 8 | "dockerBuild": { 9 | "dockerfile": "${workspaceFolder}/Dockerfile", 10 | "context": "${workspaceFolder}", 11 | "pull": true 12 | } 13 | }, 14 | { 15 | "type": "docker-run", 16 | "label": "docker-run: release", 17 | "dependsOn": [ 18 | "docker-build" 19 | ], 20 | "platform": "node" 21 | }, 22 | { 23 | "type": "docker-run", 24 | "label": "docker-run: debug", 25 | "dependsOn": [ 26 | "docker-build" 27 | ], 28 | "dockerRun": { 29 | "env": { 30 | "DEBUG": "*", 31 | "NODE_ENV": "development" 32 | } 33 | }, 34 | "node": { 35 | "enableDebugging": true 36 | } 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /SCM.Web/scm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14-alpine AS build 2 | WORKDIR /app 3 | 4 | COPY . . 5 | 6 | WORKDIR "/app/SCM.Web/scm" 7 | 8 | RUN npm install 9 | 10 | RUN npm run build --prod 11 | 12 | FROM nginx:1.17.1-alpine AS prod-stage 13 | COPY --from=build /app/SCM.Web/scm/dist/scm /usr/share/nginx/html 14 | EXPOSE 80 15 | CMD ["nginx","-g","daemon off;"] 16 | -------------------------------------------------------------------------------- /SCM.Web/scm/README.md: -------------------------------------------------------------------------------- 1 | # Scm 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.2.10. 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 Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /SCM.Web/scm/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | SELENIUM_PROMISE_MANAGER: false, 20 | baseUrl: 'http://localhost:4200/', 21 | framework: 'jasmine', 22 | jasmineNodeOpts: { 23 | showColors: true, 24 | defaultTimeoutInterval: 30000, 25 | print: function() {} 26 | }, 27 | onPrepare() { 28 | require('ts-node').register({ 29 | project: require('path').join(__dirname, './tsconfig.json') 30 | }); 31 | jasmine.getEnv().addReporter(new SpecReporter({ 32 | spec: { 33 | displayStacktrace: StacktraceOption.PRETTY 34 | } 35 | })); 36 | } 37 | }; -------------------------------------------------------------------------------- /SCM.Web/scm/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { browser, logging } from 'protractor'; 2 | import { AppPage } from './app.po'; 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', async () => { 12 | await page.navigateTo(); 13 | expect(await page.getTitleText()).toEqual('scm app is running!'); 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 | -------------------------------------------------------------------------------- /SCM.Web/scm/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | async navigateTo(): Promise { 5 | return browser.get(browser.baseUrl); 6 | } 7 | 8 | async getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SCM.Web/scm/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../out-tsc/e2e", 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SCM.Web/scm/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'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, './coverage/scm'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /SCM.Web/scm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scm", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve --host 0.0.0.0", 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": "~11.2.11", 15 | "@angular/cdk": "^11.2.11", 16 | "@angular/common": "~11.2.11", 17 | "@angular/compiler": "~11.2.11", 18 | "@angular/core": "~11.2.11", 19 | "@angular/forms": "~11.2.11", 20 | "@angular/material": "^11.2.11", 21 | "@angular/platform-browser": "~11.2.11", 22 | "@angular/platform-browser-dynamic": "~11.2.11", 23 | "@angular/router": "~11.2.11", 24 | "rxjs": "~6.6.0", 25 | "tslib": "^2.0.0", 26 | "zone.js": "~0.11.3" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "~0.1102.10", 30 | "@angular/cli": "~11.2.10", 31 | "@angular/compiler-cli": "~11.2.11", 32 | "@types/jasmine": "~3.6.0", 33 | "@types/node": "^12.11.1", 34 | "codelyzer": "^6.0.0", 35 | "jasmine-core": "~3.6.0", 36 | "jasmine-spec-reporter": "~5.0.0", 37 | "karma": "~6.1.0", 38 | "karma-chrome-launcher": "~3.1.0", 39 | "karma-coverage": "~2.0.3", 40 | "karma-jasmine": "~4.0.0", 41 | "karma-jasmine-html-reporter": "^1.5.0", 42 | "protractor": "~7.0.0", 43 | "ts-node": "~8.3.0", 44 | "tslint": "~6.1.0", 45 | "typescript": "~4.1.5" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/admin/admin.component.css: -------------------------------------------------------------------------------- 1 | mat-card 2 | { 3 | margin: 0px; 4 | padding: 0px; 5 | } 6 | 7 | mat-card-noshadow{ 8 | background: #ECECF4; 9 | box-shadow: none !important; 10 | } 11 | 12 | mat-card-content{ 13 | margin: 0px; 14 | padding: 0px; 15 | } 16 | 17 | .header 18 | { 19 | background: #E90C0C; 20 | border-bottom: 5px solid #790606; 21 | height: 50px; 22 | padding-left: 5px; 23 | } 24 | 25 | .subheader 26 | { 27 | height: 40px; 28 | background: #5B5C60; 29 | } 30 | 31 | .subheader_title{ 32 | vertical-align:baseline; 33 | padding-top: 5px; 34 | padding-bottom: 0px; 35 | padding-left: 5px; 36 | font-size: 13pt; 37 | font-family: Arial, Helvetica, sans-serif; 38 | color: #C8CCD2; 39 | } 40 | 41 | .header_title{ 42 | vertical-align:baseline; 43 | padding-top: 10px; 44 | padding-bottom: 0px; 45 | padding-left: 5px; 46 | font-size: 16pt; 47 | font-family: Arial, Helvetica, sans-serif; 48 | color: #FFFFFF; 49 | } 50 | 51 | .card_cntnt 52 | { 53 | padding: 15px; 54 | padding-bottom: 15px; 55 | } 56 | 57 | .card_footer 58 | { 59 | text-align: left; 60 | padding-left: 40px; 61 | } 62 | 63 | .frm-ctrl { 64 | width: 90%; 65 | } 66 | 67 | .icon_align 68 | { 69 | vertical-align: middle; 70 | } 71 | .phone_cntnt 72 | { 73 | font-weight: bold; 74 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/admin/admin.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | securityAdmin - Manage Site Content 6 | 7 | 8 | 9 | 10 | emoji_people User Management 11 | 12 | 13 | 14 | book Course Management 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | manage_accountsClient - Buy User Course 25 | 26 | 27 | 28 | 29 | emoji_peoplebook User Course Management 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/admin/admin.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AdminComponent } from './admin.component'; 4 | 5 | describe('AdminComponent', () => { 6 | let component: AdminComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ AdminComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AdminComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/admin/admin.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-admin', 5 | templateUrl: './admin.component.html', 6 | styleUrls: ['./admin.component.css'] 7 | }) 8 | export class AdminComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/course/course-list/course-list.component.css: -------------------------------------------------------------------------------- 1 | /* Structure */ 2 | .example-container { 3 | position: relative; 4 | min-height: 200px; 5 | } 6 | 7 | .example-table-container { 8 | position: relative; 9 | height: 400px; 10 | overflow: auto; 11 | } 12 | 13 | table { 14 | width: 100%; 15 | } 16 | 17 | .example-loading-shade { 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | bottom: 56px; 22 | right: 0; 23 | background: rgba(0, 0, 0, 0.15); 24 | z-index: 1; 25 | display: flex; 26 | align-items: center; 27 | justify-content: center; 28 | } 29 | 30 | .example-rate-limit-reached { 31 | color: #980000; 32 | max-width: 360px; 33 | text-align: center; 34 | } 35 | 36 | /* Column Widths */ 37 | .mat-column-number, 38 | .mat-column-state { 39 | max-width: 64px; 40 | } 41 | 42 | .mat-column-created { 43 | max-width: 124px; 44 | } 45 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/course/course-list/course-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CourseListComponent } from './course-list.component'; 4 | 5 | describe('CourseListComponent', () => { 6 | let component: CourseListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ CourseListComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CourseListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/course/manage-course/manage-course.component.css: -------------------------------------------------------------------------------- 1 | .header{ 2 | background: #E90C0C; 3 | border-bottom: 5px solid #790606; 4 | height: 50px; 5 | padding-left: 5px; 6 | } 7 | 8 | .header_title{ 9 | vertical-align:baseline; 10 | padding-top: 10px; 11 | padding-bottom: 0px; 12 | padding-left: 5px; 13 | font-size: 16pt; 14 | font-family: Arial, Helvetica, sans-serif; 15 | color: rgba(255, 255, 255, 0.85); 16 | } 17 | mat-card{ 18 | margin: 0px; 19 | padding: 0px; 20 | 21 | } 22 | mat-card-content{ 23 | margin: 0px; 24 | padding: 0px; 25 | width: 600px; 26 | height: 500px; 27 | } 28 | 29 | .mat-dialog-container { 30 | padding: 0 !important; 31 | margin: 0 !important; 32 | } 33 | 34 | .frm-ctrl { 35 | width: 100%; 36 | } 37 | 38 | .card_cntnt 39 | { 40 | padding: 20px; 41 | } 42 | .footer 43 | { 44 | padding-top: 20px; 45 | text-align: right; 46 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/course/manage-course/manage-course.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ManageCourseComponent } from './manage-course.component'; 4 | 5 | describe('ManageCourseComponent', () => { 6 | let component: ManageCourseComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ManageCourseComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ManageCourseComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/user/manage-user/manage-user.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhub-io/StudentCourseManagement/3a20b7abfa0e61701540c5869705d3d86759efd6/SCM.Web/scm/src/app/admin/user/manage-user/manage-user.component.css -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/user/manage-user/manage-user.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ManageUserComponent } from './manage-user.component'; 4 | 5 | describe('ManageUserComponent', () => { 6 | let component: ManageUserComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ManageUserComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ManageUserComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/user/user-list/user-list.component.css: -------------------------------------------------------------------------------- 1 | /* Structure */ 2 | .example-container { 3 | position: relative; 4 | min-height: 200px; 5 | } 6 | 7 | .example-table-container { 8 | position: relative; 9 | height: 400px; 10 | overflow: auto; 11 | } 12 | 13 | table { 14 | width: 100%; 15 | } 16 | 17 | .example-loading-shade { 18 | position: absolute; 19 | top: 0; 20 | left: 0; 21 | bottom: 56px; 22 | right: 0; 23 | background: rgba(0, 0, 0, 0.15); 24 | z-index: 1; 25 | display: flex; 26 | align-items: center; 27 | justify-content: center; 28 | } 29 | 30 | .example-rate-limit-reached { 31 | color: #980000; 32 | max-width: 360px; 33 | text-align: center; 34 | } 35 | 36 | /* Column Widths */ 37 | .mat-column-number, 38 | .mat-column-state { 39 | max-width: 64px; 40 | } 41 | 42 | .mat-column-created { 43 | max-width: 124px; 44 | } 45 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/admin/user/user-list/user-list.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { UserListComponent } from './user-list.component'; 4 | 5 | describe('UserListComponent', () => { 6 | let component: UserListComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ UserListComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(UserListComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | import { AdminComponent } from './admin/admin/admin.component'; 4 | import { CourseListComponent } from './admin/course/course-list/course-list.component'; 5 | import { UserListComponent } from './admin/user/user-list/user-list.component'; 6 | import { SelectCourseComponent } from './client/select-course/select-course.component'; 7 | 8 | const routes: Routes = [ 9 | { 10 | path: '', 11 | redirectTo: 'home', 12 | pathMatch: 'full' 13 | }, 14 | { 15 | path:'home', 16 | component: AdminComponent 17 | }, 18 | { 19 | path:'home/managecourse', 20 | component: CourseListComponent 21 | }, 22 | { 23 | path:'home/manageuser', 24 | component:UserListComponent 25 | }, 26 | { 27 | path:'home/usercourse', 28 | component:SelectCourseComponent 29 | }, 30 | ]; 31 | 32 | @NgModule({ 33 | imports: [RouterModule.forRoot(routes)], 34 | exports: [RouterModule] 35 | }) 36 | export class AppRoutingModule { } 37 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhub-io/StudentCourseManagement/3a20b7abfa0e61701540c5869705d3d86759efd6/SCM.Web/scm/src/app/app.component.css -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | }); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'scm'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.componentInstance; 26 | expect(app.title).toEqual('scm'); 27 | }); 28 | 29 | it('should render title', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.nativeElement; 33 | expect(compiled.querySelector('.content span').textContent).toContain('scm app is running!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'scm'; 10 | } 11 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/client/select-course/select-course.component.css: -------------------------------------------------------------------------------- 1 | .header{ 2 | background: #E90C0C; 3 | border-bottom: 5px solid #790606; 4 | height: 50px; 5 | padding-left: 5px; 6 | } 7 | 8 | .header_title{ 9 | vertical-align:baseline; 10 | padding-top: 10px; 11 | padding-bottom: 0px; 12 | padding-left: 5px; 13 | font-size: 16pt; 14 | font-family: Arial, Helvetica, sans-serif; 15 | color: rgba(255, 255, 255, 0.85); 16 | } 17 | mat-card{ 18 | margin: 0px; 19 | padding: 0px; 20 | 21 | } 22 | mat-card-content{ 23 | margin: 0px; 24 | padding: 0px; 25 | width: 600px; 26 | height: 200px; 27 | } 28 | 29 | .mat-dialog-container { 30 | padding: 0 !important; 31 | margin: 0 !important; 32 | } 33 | 34 | .frm-ctrl { 35 | width: 100%; 36 | } 37 | 38 | .card_cntnt 39 | { 40 | padding: 20px; 41 | } 42 | .footer 43 | { 44 | padding-top: 20px; 45 | text-align: right; 46 | } 47 | 48 | /* Structure */ 49 | .example-container { 50 | position: relative; 51 | min-height: 200px; 52 | } 53 | 54 | .example-table-container { 55 | position: relative; 56 | height: 400px; 57 | overflow: auto; 58 | } 59 | 60 | table { 61 | width: 100%; 62 | } 63 | 64 | .example-loading-shade { 65 | position: absolute; 66 | top: 0; 67 | left: 0; 68 | bottom: 56px; 69 | right: 0; 70 | background: rgba(0, 0, 0, 0.15); 71 | z-index: 1; 72 | display: flex; 73 | align-items: center; 74 | justify-content: center; 75 | } 76 | 77 | .example-rate-limit-reached { 78 | color: #980000; 79 | max-width: 360px; 80 | text-align: center; 81 | } 82 | 83 | /* Column Widths */ 84 | .mat-column-number, 85 | .mat-column-state { 86 | max-width: 64px; 87 | } 88 | 89 | .mat-column-created { 90 | max-width: 124px; 91 | } 92 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/client/select-course/select-course.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SelectCourseComponent } from './select-course.component'; 4 | 5 | describe('SelectCourseComponent', () => { 6 | let component: SelectCourseComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ SelectCourseComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SelectCourseComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/model/BoughtCourses.ts: -------------------------------------------------------------------------------- 1 | export interface IBoughtCourse { 2 | Subjects: String, 3 | TotalPrice: Number, 4 | FirstName: String, 5 | LastName: String, 6 | EmailAddress: String, 7 | PhoneNumber: String, 8 | Address: String 9 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/model/course.ts: -------------------------------------------------------------------------------- 1 | export interface ICourse { 2 | courseID:number, 3 | courseName:String, 4 | courseShortName:String, 5 | creditHour:number, 6 | price:number, 7 | dateAdded:Date, 8 | dateUpdated:Date 9 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/model/courseBasket.ts: -------------------------------------------------------------------------------- 1 | import { ICourse } from "./course"; 2 | 3 | export interface ICourseBasket{ 4 | userEmail: String, 5 | totalPrice:Number, 6 | items: ICourse[], 7 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/model/courseBasketVM.ts: -------------------------------------------------------------------------------- 1 | export interface ICourseBasketVM { 2 | userEmail: string, 3 | subjects: string, 4 | totalPrice: Number 5 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/model/selectValue.ts: -------------------------------------------------------------------------------- 1 | export interface ISelectValue { 2 | value: string; 3 | viewValue: string; 4 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/model/user.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | userID: number, 3 | salutation: String, 4 | firstName: String, 5 | lastName: String, 6 | dob: Date, 7 | age: number, 8 | gender: String, 9 | emailAddress: String, 10 | phoneNumber: String, 11 | city: String, 12 | state: String, 13 | zip: String, 14 | country: String 15 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/service/data/data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | 5 | @Injectable() 6 | export class DataService { 7 | 8 | //SERVER_URL = environment.production; 9 | 10 | constructor(public _http: HttpClient) { } 11 | 12 | get(url: string): Observable { 13 | let headers = new HttpHeaders({ 'Content-Type': 'application/json' }); 14 | return this._http.get(url, { headers: headers }); 15 | } 16 | 17 | post(url: string, model: any): Observable { 18 | let body = JSON.stringify(model); 19 | let headers = new HttpHeaders({ 'Content-Type': 'application/json' }); 20 | return this._http.post(url, body, { headers: headers }); 21 | } 22 | 23 | put(url: string, model: any): Observable { 24 | let body = JSON.stringify(model); 25 | let headers = new HttpHeaders({ 'Content-Type': 'application/json' }); 26 | return this._http.put(url, body, { headers: headers }); 27 | } 28 | 29 | delete(url: string): Observable { 30 | let headers = new HttpHeaders({ 'Content-Type': 'application/json' }); 31 | return this._http.delete(url, { headers: headers }); 32 | } 33 | 34 | upload(url: string, model: any): Observable { 35 | let body = model; 36 | let headers = new HttpHeaders({ 'Content-Type': 'multipart/form-data' }); 37 | return this._http.post(url, body); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/shared/enum.ts: -------------------------------------------------------------------------------- 1 | export enum DBOperation { 2 | create = 1, 3 | update = 2, 4 | delete =3 5 | } 6 | 7 | export enum ResponseSnackbar { 8 | Sucess = 'Sucess', 9 | Error = 'Error', 10 | Pending ='Pending' 11 | } 12 | 13 | export enum IMenuType { 14 | Main = 'Main', 15 | Sub = 'Sub' 16 | } 17 | export enum IYesNo { 18 | Yes = 'Yes', 19 | No = 'No' 20 | } 21 | export enum IActiveDead { 22 | Active = 'Active', 23 | Dead = 'Dead' 24 | } 25 | export enum IPageStatus { 26 | Published = 'Published', 27 | Draft = 'Draft', 28 | Initiated = 'Initiated', 29 | Dead = 'Dead' 30 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/shared/messages/confirm-delete/confirm-delete.component.css: -------------------------------------------------------------------------------- 1 | .header{ 2 | background: #E90C0C; 3 | border-bottom: 5px solid #790606; 4 | height: 50px; 5 | padding-left: 5px; 6 | } 7 | 8 | .header_title{ 9 | vertical-align:baseline; 10 | padding-top: 10px; 11 | padding-bottom: 0px; 12 | padding-left: 5px; 13 | font-size: 16pt; 14 | font-family: Arial, Helvetica, sans-serif; 15 | color: rgba(255, 255, 255, 0.85); 16 | } 17 | mat-card{ 18 | margin: 0px; 19 | padding: 0px; 20 | 21 | } 22 | mat-card-content{ 23 | margin: 0px; 24 | padding: 0px; 25 | 26 | height: 170px; 27 | overflow:hidden; 28 | } 29 | 30 | .mat-dialog-container { 31 | padding: 0 !important; 32 | margin: 0 !important; 33 | } 34 | 35 | .frm-ctrl { 36 | width: 100%; 37 | } 38 | 39 | .card_cntnt 40 | { 41 | padding: 20px; 42 | } 43 | .msg 44 | { 45 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 46 | font-size: large; 47 | padding-top: 10px; 48 | } 49 | .footer 50 | { 51 | padding-top: 10px; 52 | text-align: center; 53 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/shared/messages/confirm-delete/confirm-delete.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | delete Verify one more time! 4 | 5 | 6 | 7 | live_help Are you sure to delete the selected record? 8 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/shared/messages/confirm-delete/confirm-delete.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ConfirmDeleteComponent } from './confirm-delete.component'; 4 | 5 | describe('ConfirmDeleteComponent', () => { 6 | let component: ConfirmDeleteComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [ ConfirmDeleteComponent ] 12 | }) 13 | .compileComponents(); 14 | }); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ConfirmDeleteComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/shared/messages/confirm-delete/confirm-delete.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "@angular/core"; 2 | import { MatDialogRef } from '@angular/material/dialog'; 3 | 4 | @Component({ 5 | selector: 'confirm-delete-modal', 6 | templateUrl: './confirm-delete.component.html', 7 | styleUrls: ['./confirm-delete.component.css'] 8 | }) 9 | export class ConfirmDeleteComponent { 10 | 11 | constructor( 12 | public dialogRef: MatDialogRef) { } 13 | 14 | onNoClick(): void { 15 | this.dialogRef.close(); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/app/shared/util.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Injectable } from "@angular/core"; 3 | import {MatDialog} from '@angular/material/dialog'; 4 | import {MatSnackBar} from '@angular/material/snack-bar'; 5 | import { Observable } from "rxjs"; 6 | import { ResponseSnackbar } from "./enum"; 7 | import { ConfirmDeleteComponent } from "./messages/confirm-delete/confirm-delete.component"; 8 | 9 | @Injectable() 10 | export class Util { 11 | constructor(private snackBar: MatSnackBar,public dialog: MatDialog) { } 12 | 13 | openSnackBar(message: string, action: ResponseSnackbar): void { 14 | let dur = action == (ResponseSnackbar.Error || ResponseSnackbar.Pending) ? -1 : 2000; 15 | this.snackBar.open(message, action.toString(), { 16 | duration: dur, 17 | }); 18 | } 19 | 20 | confirmDelete(): Observable { 21 | let dialogRef = this.dialog.open(ConfirmDeleteComponent, { 22 | width: '500px' 23 | }); 24 | return dialogRef.afterClosed(); 25 | } 26 | 27 | getEnumArray(enumObj: any) { 28 | return Object.keys(enumObj).map(function (type) { 29 | return enumObj[type]; 30 | }); 31 | } 32 | } -------------------------------------------------------------------------------- /SCM.Web/scm/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhub-io/StudentCourseManagement/3a20b7abfa0e61701540c5869705d3d86759efd6/SCM.Web/scm/src/assets/.gitkeep -------------------------------------------------------------------------------- /SCM.Web/scm/src/assets/images/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhub-io/StudentCourseManagement/3a20b7abfa0e61701540c5869705d3d86759efd6/SCM.Web/scm/src/assets/images/banner.jpg -------------------------------------------------------------------------------- /SCM.Web/scm/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /SCM.Web/scm/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 | 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 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhub-io/StudentCourseManagement/3a20b7abfa0e61701540c5869705d3d86759efd6/SCM.Web/scm/src/favicon.ico -------------------------------------------------------------------------------- /SCM.Web/scm/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Scm K8 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /SCM.Web/scm/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 | -------------------------------------------------------------------------------- /SCM.Web/scm/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | html, body { height: 100%; } 4 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } 5 | .div-spacing{ 6 | padding:10px; 7 | } -------------------------------------------------------------------------------- /SCM.Web/scm/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: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /SCM.Web/scm/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /SCM.Web/scm/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "sourceMap": true, 12 | "declaration": false, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "moduleResolution": "node", 16 | "importHelpers": true, 17 | "target": "es2015", 18 | "module": "es2020", 19 | "lib": [ 20 | "es2018", 21 | "dom" 22 | ] 23 | }, 24 | "angularCompilerOptions": { 25 | "enableI18nLegacyMessageIdFormat": false, 26 | "strictInjectionParameters": true, 27 | "strictInputAccessModifiers": true, 28 | "strictTemplates": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SCM.Web/scm/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /StudentCourse/db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/mssql/server:2017-CU24-ubuntu-16.04 2 | COPY ./StudentCourse/db/data / 3 | RUN chmod +x ./SqlCmdStartup.sh 4 | CMD /bin/bash ./entrypoint.sh -------------------------------------------------------------------------------- /StudentCourse/db/data/SqlCmdStartup.sh: -------------------------------------------------------------------------------- 1 | sleep 15 2 | /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Yasser_123 -d master -i studentcoursedb.sql -------------------------------------------------------------------------------- /StudentCourse/db/data/entrypoint.sh: -------------------------------------------------------------------------------- 1 | ./SqlCmdStartup.sh & /opt/mssql/bin/sqlservr -------------------------------------------------------------------------------- /StudentCourse/db/data/studentcoursedb.sql: -------------------------------------------------------------------------------- 1 | USE [master] 2 | GO 3 | CREATE DATABASE [StudentCourses] 4 | GO 5 | USE [StudentCourses] 6 | CREATE TABLE [dbo].[StudentCourses]( 7 | [StudentCourseID] [int] IDENTITY(1,1) NOT NULL, 8 | [Subjects] [varchar](5000) NOT NULL, 9 | [TotalPrice] [decimal](18, 2) NOT NULL, 10 | [FirstName] [varchar](50) NOT NULL, 11 | [LastName] [varchar](50) NOT NULL, 12 | [EmailAddress] [varchar](100) NOT NULL, 13 | [PhoneNumber] [varchar](20) NOT NULL, 14 | [Address] [varchar](500) NOT NULL, 15 | [SessionStartDate] [datetime] NOT NULL, 16 | [SessionEndDate] [datetime] NOT NULL, 17 | [DateAdded] [datetime] NOT NULL, 18 | [DateUpdated] [datetime] NULL, 19 | CONSTRAINT [PK_StudentCourses] PRIMARY KEY CLUSTERED 20 | ( 21 | [StudentCourseID] ASC 22 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 23 | ) ON [PRIMARY] 24 | GO 25 | USE [master] 26 | GO 27 | ALTER DATABASE [StudentCourses] SET READ_WRITE 28 | GO 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.API.Controllers 2 | { 3 | using MediatR; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | [Route("api/[controller]")] 8 | [ApiController] 9 | public class BaseController : ControllerBase 10 | { 11 | private IMediator mediator; 12 | 13 | /// 14 | /// Gets the Mediator. 15 | /// 16 | protected IMediator Mediator => this.mediator ??= this.HttpContext.RequestServices.GetService(); 17 | } 18 | } -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/Controllers/StudentControllerController.cs: -------------------------------------------------------------------------------- 1 | using Course.Application.User.Commands; 2 | using Microsoft.AspNetCore.Mvc; 3 | using StudentCourse.Application.StudentCourse.DTO; 4 | using StudentCourse.Application.StudentCourse.VM; 5 | using StudentCourse.Application.User.Commands; 6 | using StudentCourse.Application.User.Queries; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace StudentCourse.API.Controllers 11 | { 12 | [Route("api/[controller]")] 13 | [ApiController] 14 | public class StudentCourseController : BaseController 15 | { 16 | 17 | //[HttpGet("{id}")] 18 | //public async Task> Get(int id) 19 | //{ 20 | // return await this.Mediator.Send(new GetSingleCourseQuery { CourseID = (int)id }); 21 | //} 22 | 23 | [HttpGet] 24 | public async Task> Get() 25 | { 26 | return await this.Mediator.Send(new GetAllStudentCourseQuery()); 27 | } 28 | 29 | [HttpPost] 30 | public async Task> Post(List courses) 31 | { 32 | return await this.Mediator.Send(new AddStudentCourseCommand() {Courses = courses }); 33 | } 34 | 35 | //[HttpPut] 36 | //public async Task> Put(UpdateCourseCommand command) 37 | //{ 38 | // return await this.Mediator.Send(command); 39 | //} 40 | 41 | [HttpDelete("{id}")] 42 | public async Task> Delete(int id) 43 | { 44 | return await this.Mediator.Send(new DeleteStudentCourseCommand { StudentCourseID = id }); 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 8 | WORKDIR /src 9 | COPY ["StudentCourse/src/StudentCourse.API/StudentCourse.API.csproj", "StudentCourse/src/StudentCourse.API/"] 10 | RUN dotnet restore "StudentCourse/src/StudentCourse.API/StudentCourse.API.csproj" 11 | COPY . . 12 | WORKDIR "/src/StudentCourse/src/StudentCourse.API" 13 | RUN dotnet build "StudentCourse.API.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "StudentCourse.API.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "StudentCourse.API.dll"] -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace StudentCourse.API 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:17742", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "StudentCourse.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5000" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/StudentCourse.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Linux 6 | ..\..\.. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "StudentCourseConnection": "Server=studentcoursedb;Database=StudentCourses;User Id=sa;Password=Yasser_123;" 4 | }, 5 | "AppSettings": { 6 | "LongRunningProcessMilliseconds": "1500", 7 | "MSG_COURSE_COURSENAME": "Course Name is required!", 8 | "MSG_COURSE_NULLCREDITHOUR": "Credit Hour is required!", 9 | "MSG_COURSE_COURSEID": "Course ID is required!" 10 | }, 11 | "Logging": { 12 | "LogLevel": { 13 | "Default": "Information", 14 | "Microsoft": "Warning", 15 | "Microsoft.Hosting.Lifetime": "Information" 16 | } 17 | }, 18 | "AllowedHosts": "*", 19 | "EventBus": { 20 | "HostName": "rabbitmq", 21 | "UserName": "guest", 22 | "Password": "guest" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/BaseClass/ApplicationBase.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using global::StudentCourse.Application.Common.Interfaces; 3 | using StudentCourse.Domain.UnitOfWork; 4 | 5 | namespace StudentCourse.Application.Common.BaseClass 6 | { 7 | public class ApplicationBase 8 | { 9 | public IUnitOfWork UnitOfWork { get; set; } 10 | public IConfigConstants ConfigConstants { get; set; } 11 | public IMapper Mapper { get; set; } 12 | 13 | public ApplicationBase(IConfigConstants configConstants, IUnitOfWork unitOfWork, IMapper mapper) 14 | { 15 | ConfigConstants = configConstants; 16 | UnitOfWork = unitOfWork; 17 | Mapper = mapper; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Behaviors/LoggingBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Behaviors 2 | { 3 | using MediatR.Pipeline; 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | public class LoggingBehaviour : IRequestPreProcessor 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public LoggingBehaviour(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public async Task Process(TRequest request, CancellationToken cancellationToken) 18 | { 19 | var requestName = typeof(TRequest).Name; 20 | string userName = string.Empty; 21 | 22 | await Task.Run(() => _logger.LogInformation("UserManagement Request: {Name} {@UserName} {@Request}", 23 | requestName, userName, request)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Behaviors/PerformanceBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Behaviors 2 | { 3 | using global::StudentCourse.Application.Common.Interfaces; 4 | using MediatR; 5 | using Microsoft.Extensions.Logging; 6 | using System.Diagnostics; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | public class PerformanceBehaviour : IPipelineBehavior 11 | { 12 | private readonly Stopwatch _timer; 13 | private readonly ILogger _logger; 14 | private readonly IConfigConstants _constact; 15 | 16 | public PerformanceBehaviour(ILogger logger, IConfigConstants constants) 17 | { 18 | _timer = new Stopwatch(); 19 | _logger = logger; 20 | _constact = constants; 21 | } 22 | 23 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 24 | { 25 | _timer.Start(); 26 | 27 | var response = await next(); 28 | 29 | _timer.Stop(); 30 | 31 | var elapsedMilliseconds = _timer.ElapsedMilliseconds; 32 | 33 | if (elapsedMilliseconds > _constact.LongRunningProcessMilliseconds) 34 | { 35 | var requestName = typeof(TRequest).Name; 36 | var userName = string.Empty; 37 | _logger.LogWarning("UserManagement Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {@UserName} {@Request}", 38 | requestName, elapsedMilliseconds, userName, request); 39 | } 40 | 41 | return response; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Behaviors/UnhandledExceptionBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Behaviours 2 | { 3 | using MediatR; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | public class UnhandledExceptionBehaviour : IPipelineBehavior 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public UnhandledExceptionBehaviour(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 18 | { 19 | try 20 | { 21 | return await next(); 22 | } 23 | catch (Exception ex) 24 | { 25 | var requestName = typeof(TRequest).Name; 26 | 27 | _logger.LogError(ex, "UserManagement Request: Unhandled Exception for Request {Name} {@Request}", requestName, request); 28 | 29 | throw; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Behaviors/ValidationBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Behaviours 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using FluentValidation; 8 | using MediatR; 9 | using ValidationException = Exceptions.ValidationException; 10 | public class ValidationBehaviour : IPipelineBehavior 11 | where TRequest : IRequest 12 | { 13 | private readonly IEnumerable> _validators; 14 | 15 | public ValidationBehaviour(IEnumerable> validators) 16 | { 17 | _validators = validators; 18 | } 19 | 20 | public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) 21 | { 22 | if (_validators.Any()) 23 | { 24 | var context = new ValidationContext(request); 25 | 26 | var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken))); 27 | var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList(); 28 | 29 | if (failures.Count != 0) 30 | throw new ValidationException(failures); 31 | } 32 | return await next(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Exceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Exceptions 2 | { 3 | using System; 4 | public class NotFoundException : Exception 5 | { 6 | public NotFoundException() 7 | : base() 8 | { 9 | } 10 | 11 | public NotFoundException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public NotFoundException(string message, Exception innerException) 17 | : base(message, innerException) 18 | { 19 | } 20 | 21 | public NotFoundException(string name, object key) 22 | : base($"Entity \"{name}\" ({key}) was not found.") 23 | { 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Exceptions/ValidationException.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Exceptions 2 | { 3 | using FluentValidation.Results; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | public class ValidationException : Exception 8 | { 9 | public ValidationException() 10 | : base("One or more validation failures have occurred.") 11 | { 12 | Errors = new Dictionary(); 13 | } 14 | 15 | public ValidationException(IEnumerable failures) 16 | : this() 17 | { 18 | Errors = failures 19 | .GroupBy(e => e.PropertyName, e => e.ErrorMessage) 20 | .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); 21 | } 22 | 23 | public IDictionary Errors { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Interfaces/IConfigConstants.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Interfaces 2 | { 3 | public interface IConfigConstants 4 | { 5 | string StudentCourseConnection { get; } 6 | string TestFullStackConnection { get; } 7 | int LongRunningProcessMilliseconds { get; } 8 | string MSG_COURSE_COURSENAME { get; } 9 | string MSG_COURSE_NULLCREDITHOUR { get; } 10 | string MSG_COURSE_COURSEID { get; } 11 | 12 | string MSG_SC_NULLStdCourseID { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Mappings/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Mappings 2 | { 3 | using AutoMapper; 4 | public interface IMapFrom 5 | { 6 | void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType()).ReverseMap(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/Common/Mappings/MappingProfile.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.Common.Mappings 2 | { 3 | using AutoMapper; 4 | using System; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | public class MappingProfile : Profile 9 | { 10 | public MappingProfile() 11 | { 12 | ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly()); 13 | } 14 | 15 | private void ApplyMappingsFromAssembly(Assembly assembly) 16 | { 17 | var types = assembly.GetExportedTypes() 18 | .Where(t => t.GetInterfaces().Any(i => 19 | i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>))) 20 | .ToList(); 21 | 22 | foreach (var type in types) 23 | { 24 | var instance = Activator.CreateInstance(type); 25 | 26 | var methodInfo = type.GetMethod("Mapping") 27 | ?? type.GetInterface("IMapFrom`1").GetMethod("Mapping"); 28 | 29 | methodInfo?.Invoke(instance, new object[] { this }); 30 | 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/RabbitMQ/ApplicationBuilderExtentions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.Extensions.Hosting; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace StudentCourse.Application.RabbitMQ 6 | { 7 | public static class ApplicationBuilderExtentions 8 | { 9 | public static EventBusRabbitMQConsumer Listener { get; set; } 10 | 11 | public static IApplicationBuilder UseRabbitListener(this IApplicationBuilder app) 12 | { 13 | Listener = app.ApplicationServices.GetService(); 14 | var life = app.ApplicationServices.GetService(); 15 | 16 | life.ApplicationStarted.Register(OnStarted); 17 | life.ApplicationStopping.Register(OnStopping); 18 | 19 | return app; 20 | } 21 | 22 | private static void OnStarted() 23 | { 24 | Listener.Consume(); 25 | } 26 | 27 | private static void OnStopping() 28 | { 29 | Listener.Disconnect(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/Commands/DeleteStudentCourseCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using AutoMapper; 4 | using MediatR; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using StudentCourse.Application.Common.BaseClass; 8 | using StudentCourse.Domain.UnitOfWork; 9 | using StudentCourse.Application.Common.Interfaces; 10 | 11 | public class DeleteStudentCourseCommand : IRequest 12 | { 13 | public int StudentCourseID { get; set; } 14 | public class DeleteStudentCourseHandler : ApplicationBase, IRequestHandler 15 | { 16 | public DeleteStudentCourseHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 17 | : base(constant, unitOfWork, mapper) 18 | { 19 | } 20 | 21 | public async Task Handle(DeleteStudentCourseCommand request, CancellationToken cancellationToken) 22 | { 23 | this.UnitOfWork.StartTransaction(); 24 | var res = UnitOfWork.UserCourse.DeleteStudentCourse(request.StudentCourseID).Result; 25 | this.UnitOfWork.Commit(); 26 | return await Task.Run(() => res, cancellationToken); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/Commands/DeleteStudentCourseCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using FluentValidation; 4 | using StudentCourse.Application.Common.Interfaces; 5 | 6 | public class DeleteStudentCourseCommandValidator : AbstractValidator 7 | { 8 | public DeleteStudentCourseCommandValidator(IConfigConstants constant) 9 | { 10 | this.RuleFor(v => v.StudentCourseID).GreaterThan(0).WithMessage(constant.MSG_SC_NULLStdCourseID); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/Commands/UpdateStudentCourseCommandValidator.cs: -------------------------------------------------------------------------------- 1 | namespace Course.Application.User.Commands 2 | { 3 | using FluentValidation; 4 | using StudentCourse.Application.Common.Interfaces; 5 | 6 | public class UpdateStudentCourseCommandValidator : AbstractValidator 7 | { 8 | public UpdateStudentCourseCommandValidator(IConfigConstants constant) 9 | { 10 | this.RuleFor(v => v.CourseName).NotEmpty().WithMessage(constant.MSG_COURSE_COURSENAME); 11 | this.RuleFor(v => v.CreditHour).GreaterThan(0).WithMessage(constant.MSG_COURSE_NULLCREDITHOUR); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/DTO/StudentCourseDTO.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using System; 3 | using StudentCourse.Application.Common.Mappings; 4 | using StudentCourseEntity = StudentCourse.Domain.Entities.StudentCourse; 5 | using EventBusRabbitMQ.Events; 6 | 7 | namespace StudentCourse.Application.StudentCourse.DTO 8 | { 9 | public class StudentCourseDTO : IMapFrom, IMapFrom 10 | { 11 | public int StudentCourseID { get; set; } 12 | public string Subjects { get; set; } 13 | public decimal TotalPrice { get; set; } 14 | public string FirstName { get; set; } 15 | public string LastName { get; set; } 16 | public string EmailAddress { get; set; } 17 | public string PhoneNumber { get; set; } 18 | public string Address { get; set; } 19 | 20 | public DateTime SessionStartDate { get; set; } 21 | 22 | public DateTime SessionEndDate { get; set; } 23 | 24 | public DateTime DateAdded { get; set; } 25 | 26 | public DateTime DateUpdated { get; set; } 27 | public void Mapping(Profile profile) 28 | { 29 | profile.CreateMap(); 30 | profile.CreateMap(); 31 | 32 | profile.CreateMap(); 33 | profile.CreateMap(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/Queries/GetAllStudentCourseQuery.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.User.Queries 2 | { 3 | using AutoMapper; 4 | using global::StudentCourse.Application.Common.BaseClass; 5 | using global::StudentCourse.Application.Common.Interfaces; 6 | using global::StudentCourse.Application.StudentCourse.DTO; 7 | using global::StudentCourse.Application.StudentCourse.VM; 8 | using global::StudentCourse.Domain.UnitOfWork; 9 | using MediatR; 10 | using System.Collections.Generic; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | 14 | public class GetAllStudentCourseQuery : IRequest 15 | { 16 | public class GetAllStudentCourseHandler : ApplicationBase, IRequestHandler 17 | { 18 | public GetAllStudentCourseHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 19 | : base(constant, unitOfWork, mapper) 20 | { 21 | } 22 | 23 | public async Task Handle(GetAllStudentCourseQuery request, CancellationToken cancellationToken) 24 | { 25 | var res = Mapper.Map(UnitOfWork.UserCourse.GetAllStudentCourse().Result, new List()); 26 | var rr = new StudentCourseVM() { CourseList = res }; 27 | return await Task.FromResult(new StudentCourseVM() { CourseList = res }); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/Queries/GetSingleStudentCourseQuery.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.User.Queries 2 | { 3 | using AutoMapper; 4 | using global::StudentCourse.Application.Common.BaseClass; 5 | using global::StudentCourse.Application.Common.Interfaces; 6 | using global::StudentCourse.Application.StudentCourse.VM; 7 | using global::StudentCourse.Domain.UnitOfWork; 8 | using MediatR; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | public class GetSingleStudentCourseQuery : IRequest 13 | { 14 | public int CourseID { get; set; } 15 | public class GetSingleUserHandler : ApplicationBase, IRequestHandler 16 | { 17 | public GetSingleUserHandler(IConfigConstants constant, IMapper mapper, IUnitOfWork unitOfWork) 18 | : base(constant, unitOfWork, mapper) 19 | { 20 | } 21 | 22 | public async Task Handle(GetSingleStudentCourseQuery request, CancellationToken cancellationToken) 23 | { 24 | //var res = this.Mapper.Map(this.UnitOfWork.Users.GetCourse(request.CourseID).Result, new CourseDTO()); 25 | //return await Task.FromResult(new CourseVM() { CourseList = new List { res } }); 26 | return await Task.Run(() => new StudentCourseVM(), cancellationToken); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/Queries/GetSingleStudentCourseQueryValidator.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Application.User.Queries 2 | { 3 | using FluentValidation; 4 | using global::StudentCourse.Application.Common.Interfaces; 5 | 6 | public class GetSingleStudentCourseQueryValidator : AbstractValidator 7 | { 8 | public GetSingleStudentCourseQueryValidator(IConfigConstants constant) 9 | { 10 | this.RuleFor(v => v.CourseID).GreaterThan(0).WithMessage(constant.MSG_COURSE_COURSEID); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Application/StudentCourse/VM/StudentCourseVM.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using StudentCourse.Application.StudentCourse.DTO; 3 | 4 | namespace StudentCourse.Application.StudentCourse.VM 5 | { 6 | public class StudentCourseVM 7 | { 8 | public IList CourseList { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Domain/Entities/StudentCourse.cs: -------------------------------------------------------------------------------- 1 | using Dapper.Contrib.Extensions; 2 | using System; 3 | 4 | namespace StudentCourse.Domain.Entities 5 | { 6 | [Table("[StudentCourses]")] 7 | public class StudentCourse 8 | { 9 | 10 | [Key] 11 | public int StudentCourseID { get; set; } 12 | public string Subjects { get; set; } 13 | public decimal TotalPrice { get; set; } 14 | public string FirstName { get; set; } 15 | public string LastName { get; set; } 16 | public string EmailAddress { get; set; } 17 | public string PhoneNumber { get; set; } 18 | public string Address { get; set; } 19 | public DateTime SessionStartDate { get; set; } 20 | public DateTime SessionEndDate { get; set; } 21 | public DateTime DateAdded { get; set; } 22 | public DateTime? DateUpdated { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Domain/Repositories/IStudentCourseRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | namespace StudentCourse.Domain.Repositories 4 | { 5 | using StudentCourseEntity = StudentCourse.Domain.Entities.StudentCourse; 6 | public interface IStudentCourseRepository 7 | { 8 | Task AddStudentCourse(StudentCourseEntity course); 9 | Task UpdateStudentCourse(StudentCourseEntity course); 10 | Task DeleteStudentCourse(int studentCourseId); 11 | Task> GetAllStudentCourse(); 12 | Task GetStudentCourse(long studentCourseId); 13 | Task DeleteAllStudentCourse(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Domain/StudentCourse.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Domain/UnitOfWork/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using StudentCourse.Domain.Repositories; 2 | 3 | namespace StudentCourse.Domain.UnitOfWork 4 | { 5 | public interface IUnitOfWork 6 | { 7 | IStudentCourseRepository UserCourse { get; } 8 | void StartTransaction(); 9 | void Commit(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Persistence/Constant/ConfigConstants.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using StudentCourse.Application.Common.Interfaces; 3 | 4 | namespace StudentCourse.Persistence.Constant 5 | { 6 | public class ConfigConstants : IConfigConstants 7 | { 8 | public IConfiguration Configuration { get; } 9 | public ConfigConstants(IConfiguration configuration) 10 | { 11 | this.Configuration = configuration; 12 | } 13 | public string StudentCourseConnection => this.Configuration.GetConnectionString("StudentCourseConnection"); 14 | 15 | public string TestFullStackConnection => this.Configuration.GetConnectionString("TestFullStackConnection"); 16 | public int LongRunningProcessMilliseconds => int.Parse(this.Configuration["AppSettings:LongRunningProcessMilliseconds"]); 17 | 18 | public string MSG_COURSE_COURSENAME => this.Configuration["AppSettings:MSG_COURSE_COURSENAME"]; 19 | 20 | public string MSG_COURSE_NULLCREDITHOUR => this.Configuration["AppSettings:MSG_COURSE_NULLCREDITHOUR"]; 21 | 22 | public string MSG_COURSE_COURSEID => this.Configuration["AppSettings:MSG_COURSE_COURSEID"]; 23 | 24 | public string MSG_SC_NULLStdCourseID => this.Configuration["AppSettings:MSG_SC_NULLStdCourseID"]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Persistence/DependencyInjection.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Persistence 2 | { 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System.Data; 5 | using System.Data.SqlClient; 6 | using StudentCourse.Domain.UnitOfWork; 7 | using StudentCourse.Application.Common.Interfaces; 8 | using StudentCourse.Persistence.Constant; 9 | 10 | public static class DependencyInjection 11 | { 12 | public static IServiceCollection AddPersistance(this IServiceCollection services) 13 | { 14 | services.AddSingleton(); 15 | services.AddSingleton(conn => new SqlConnection(conn.GetService().StudentCourseConnection)); 16 | services.AddTransient(uof => new UnitOfWork.UnitOfWork(uof.GetService())); 17 | return services; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Persistence/Repositories/Repository.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | 3 | namespace StudentCourse.Persistence.Repositories 4 | { 5 | public class Repository 6 | { 7 | protected IDbConnection DbConnection { get; } 8 | protected IDbTransaction DbTransaction { get; } 9 | public Repository(IDbConnection dbConnection, IDbTransaction dbTransaction) 10 | { 11 | DbTransaction = dbTransaction; 12 | DbConnection = dbConnection; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Persistence/Repositories/StudentCourseRepository.cs: -------------------------------------------------------------------------------- 1 | namespace StudentCourse.Persistence.Repositories 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | using System.Threading.Tasks; 7 | using Dapper.Contrib.Extensions; 8 | using StudentCourse.Domain.Entities; 9 | using StudentCourse.Domain.Repositories; 10 | 11 | public class StudentCourseRepository : Repository, IStudentCourseRepository 12 | { 13 | public StudentCourseRepository(IDbConnection dbConnection, IDbTransaction dbtransaction) 14 | : base(dbConnection, dbtransaction) 15 | { 16 | } 17 | 18 | public Task AddStudentCourse(StudentCourse course) 19 | { 20 | course.DateAdded = DateTime.Now; 21 | course.DateUpdated = null; 22 | return DbConnection.InsertAsync(course, DbTransaction); 23 | } 24 | 25 | public Task DeleteAllStudentCourse() 26 | { 27 | throw new System.NotImplementedException(); 28 | } 29 | 30 | public Task DeleteStudentCourse(int studentCourseId) 31 | { 32 | return DbConnection.DeleteAsync(new StudentCourse { StudentCourseID = studentCourseId }, DbTransaction); 33 | } 34 | 35 | public Task> GetAllStudentCourse() 36 | { 37 | return DbConnection.GetAllAsync(); 38 | } 39 | 40 | public Task GetStudentCourse(long courseId) 41 | { 42 | throw new System.NotImplementedException(); 43 | } 44 | 45 | public Task UpdateStudentCourse(StudentCourse course) 46 | { 47 | throw new System.NotImplementedException(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Persistence/StudentCourse.Persistence.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /StudentCourse/src/StudentCourse.Persistence/UnitOfWork/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using StudentCourse.Domain.Repositories; 3 | using StudentCourse.Domain.UnitOfWork; 4 | using StudentCourse.Persistence.Repositories; 5 | 6 | namespace StudentCourse.Persistence.UnitOfWork 7 | { 8 | public class UnitOfWork : IUnitOfWork 9 | { 10 | private readonly IDbConnection dbConnection; 11 | private IDbTransaction transaction; 12 | public UnitOfWork(IDbConnection dbConnection) 13 | { 14 | this.dbConnection = dbConnection; 15 | this.ManageConnection(); 16 | } 17 | public IStudentCourseRepository UserCourse => new StudentCourseRepository(this.dbConnection, this.transaction); 18 | 19 | public void StartTransaction() 20 | { 21 | 22 | if (this.transaction == null) 23 | { 24 | this.transaction = this.dbConnection.BeginTransaction(); 25 | } 26 | } 27 | public void Commit() 28 | { 29 | try 30 | { 31 | this.transaction.Commit(); 32 | } 33 | catch 34 | { 35 | this.transaction.Rollback(); 36 | } 37 | } 38 | 39 | private void ManageConnection() 40 | { 41 | if (this.dbConnection.State == ConnectionState.Closed) 42 | { 43 | this.dbConnection.Open(); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /User/db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/mssql/server:2017-CU24-ubuntu-16.04 2 | COPY ./User/db/data / 3 | RUN chmod +x ./SqlCmdStartup.sh 4 | CMD /bin/bash ./entrypoint.sh -------------------------------------------------------------------------------- /User/db/data/SqlCmdStartup.sh: -------------------------------------------------------------------------------- 1 | sleep 15 2 | /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Yasser_123 -d master -i userdb.sql -------------------------------------------------------------------------------- /User/db/data/entrypoint.sh: -------------------------------------------------------------------------------- 1 | ./SqlCmdStartup.sh & /opt/mssql/bin/sqlservr -------------------------------------------------------------------------------- /User/db/data/userdb.sql: -------------------------------------------------------------------------------- 1 | USE [master] 2 | GO 3 | CREATE DATABASE [UserManagement] 4 | GO 5 | USE [UserManagement] 6 | CREATE TABLE [dbo].[User]( 7 | [UserID] [bigint] IDENTITY(1,1) NOT NULL, 8 | [FirstName] [varchar](50) NOT NULL, 9 | [LastName] [varchar](50) NOT NULL, 10 | [DOB] [datetime] NOT NULL, 11 | [Gender] [char](1) NOT NULL, 12 | [EmailAddress] [varchar](100) NOT NULL, 13 | [PhoneNumber] [varchar](20) NULL, 14 | [City] [varchar](50) NOT NULL, 15 | [State] [varchar](50) NOT NULL, 16 | [Zip] [numeric](10, 0) NOT NULL, 17 | [Country] [varchar](50) NOT NULL, 18 | [DateAdded] [datetime] NOT NULL, 19 | [DateUpdated] [datetime] NULL, 20 | CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 21 | ( 22 | [UserID] ASC 23 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 24 | ) ON [PRIMARY] 25 | GO 26 | ALTER TABLE [dbo].[User] ADD CONSTRAINT [DF_User_DateUpdated] DEFAULT (NULL) FOR [DateUpdated] 27 | GO 28 | USE [master] 29 | GO 30 | ALTER DATABASE [UserManagement] SET READ_WRITE 31 | GO 32 | -------------------------------------------------------------------------------- /User/src/User.API/Controllers/BaseController.cs: -------------------------------------------------------------------------------- 1 | namespace User.API.Controllers 2 | { 3 | using MediatR; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | [Route("api/[controller]")] 8 | [ApiController] 9 | public class BaseController : ControllerBase 10 | { 11 | private IMediator mediator; 12 | 13 | /// 14 | /// Gets the Mediator. 15 | /// 16 | protected IMediator Mediator => this.mediator ??= this.HttpContext.RequestServices.GetService(); 17 | } 18 | } -------------------------------------------------------------------------------- /User/src/User.API/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | namespace User.API.Controllers 2 | { 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Mvc; 5 | using User.Application.User.Commands; 6 | using User.Application.User.Queries; 7 | using User.Application.User.VM; 8 | 9 | [Route("api/[controller]")] 10 | [ApiController] 11 | public class UserController : BaseController 12 | { 13 | [HttpGet("{id}")] 14 | public async Task> Get(int id) 15 | { 16 | return await this.Mediator.Send(new GetSingleUserQuery { UserID = id }); 17 | } 18 | 19 | [HttpGet] 20 | public async Task> Get() 21 | { 22 | return await this.Mediator.Send(new GetAllUserQuery()); 23 | } 24 | 25 | [HttpPost] 26 | public async Task> Post(AddUserCommand command) 27 | { 28 | return await this.Mediator.Send(command); 29 | } 30 | 31 | [HttpPut] 32 | public async Task> Put(UpdateUserCommand command) 33 | { 34 | return await this.Mediator.Send(command); 35 | } 36 | 37 | [HttpDelete("{id}")] 38 | public async Task> Delete(int id) 39 | { 40 | return await this.Mediator.Send(new DeleteUserCommand { UserID = id }); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /User/src/User.API/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base 4 | WORKDIR /app 5 | EXPOSE 80 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 8 | WORKDIR /src 9 | COPY ["User/src/User.API/User.API.csproj", "User/src/User.API/"] 10 | RUN dotnet restore "User/src/User.API/User.API.csproj" 11 | COPY . . 12 | WORKDIR "/src/User/src/User.API" 13 | RUN dotnet build "User.API.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "User.API.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "User.API.dll"] -------------------------------------------------------------------------------- /User/src/User.API/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace User.API 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /User/src/User.API/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:52614", 7 | "sslPort": 0 8 | } 9 | }, 10 | "$schema": "http://json.schemastore.org/launchsettings.json", 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "User.API": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | }, 27 | "dotnetRunMessages": "true", 28 | "applicationUrl": "http://localhost:5000" 29 | }, 30 | "Docker": { 31 | "commandName": "Docker", 32 | "launchBrowser": true, 33 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 34 | "publishAllPorts": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /User/src/User.API/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace User.API 2 | { 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.OpenApi.Models; 9 | using User.Application; 10 | using User.Persistence; 11 | public class Startup 12 | { 13 | public Startup(IConfiguration configuration) 14 | { 15 | Configuration = configuration; 16 | } 17 | 18 | public IConfiguration Configuration { get; } 19 | 20 | // This method gets called by the runtime. Use this method to add services to the container. 21 | public void ConfigureServices(IServiceCollection services) 22 | { 23 | services.AddApplication(); 24 | services.AddPersistance(); 25 | services.AddControllers(); 26 | services.AddSwaggerGen(c => 27 | { 28 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "User.API", Version = "v1" }); 29 | }); 30 | } 31 | 32 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 33 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 34 | { 35 | if (env.IsDevelopment()) 36 | { 37 | app.UseDeveloperExceptionPage(); 38 | app.UseSwagger(); 39 | app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "User.API v1")); 40 | } 41 | 42 | app.UseRouting(); 43 | app.UseAuthorization(); 44 | 45 | app.UseEndpoints(endpoints => 46 | { 47 | endpoints.MapControllers(); 48 | }); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /User/src/User.API/User.API.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Linux 6 | ..\..\.. 7 | bfd5f986-08e2-4179-b452-ef8fcb4546c4 8 | ..\..\..\docker-compose.dcproj 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /User/src/User.API/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /User/src/User.API/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "UserConnection": "Server=userdb;Database=UserManagement;User Id=sa;Password=Yasser_123;" 4 | }, 5 | "AppSettings": { 6 | "LongRunningProcessMilliseconds": "1500", 7 | "MSG_USER_NULLUSERID": "User ID is required!", 8 | "MSG_USER_NULLFIRSTNAME": "First Name is required!", 9 | "MSG_USER_NULLLASTNAME": "Last Name is required!", 10 | "MSG_USER_NULLDOB": "Date of birth is required!", 11 | "MSG_USER_NULLGENDER": "Gender is required!", 12 | "MSG_USER_GENDER_LEN": "Gender can be only M/F!", 13 | "MSG_USER_NULLEMAILADDR": "Email Address is required!", 14 | "MSG_USER_NULLPHNUM": "Phone Number is required!", 15 | "MSG_USER_NULLCITY": "City is required!", 16 | "MSG_USER_NULLSTATE": "State is required!", 17 | "MSG_USER_NULLCOUNTRY": "Country is required!" 18 | }, 19 | "Logging": { 20 | "LogLevel": { 21 | "Default": "Information", 22 | "Microsoft": "Warning", 23 | "Microsoft.Hosting.Lifetime": "Information" 24 | } 25 | }, 26 | "AllowedHosts": "*" 27 | } -------------------------------------------------------------------------------- /User/src/User.Application/Common/BaseClass/ApplicationBase.cs: -------------------------------------------------------------------------------- 1 | namespace User.Application.Common.BaseClass 2 | { 3 | using AutoMapper; 4 | using global::User.Application.Common.Interfaces; 5 | using global::User.Domain.UnitOfWork; 6 | 7 | public class ApplicationBase 8 | { 9 | public IUnitOfWork UnitOfWork { get; set; } 10 | public IConfigConstants ConfigConstants { get; set; } 11 | public IMapper Mapper { get; set; } 12 | 13 | public ApplicationBase(IConfigConstants configConstants, IUnitOfWork unitOfWork, IMapper mapper) 14 | { 15 | ConfigConstants = configConstants; 16 | UnitOfWork = unitOfWork; 17 | Mapper = mapper; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /User/src/User.Application/Common/Behaviors/LoggingBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace User.Application.Common.Behaviors 2 | { 3 | using MediatR.Pipeline; 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | public class LoggingBehaviour : IRequestPreProcessor 9 | { 10 | private readonly ILogger _logger; 11 | 12 | public LoggingBehaviour(ILogger logger) 13 | { 14 | _logger = logger; 15 | } 16 | 17 | public async Task Process(TRequest request, CancellationToken cancellationToken) 18 | { 19 | var requestName = typeof(TRequest).Name; 20 | string userName = string.Empty; 21 | 22 | await Task.Run(() => _logger.LogInformation("UserManagement Request: {Name} {@UserName} {@Request}", 23 | requestName, userName, request)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /User/src/User.Application/Common/Behaviors/PerformanceBehaviour.cs: -------------------------------------------------------------------------------- 1 | namespace User.Application.Common.Behaviors 2 | { 3 | using global::User.Application.Common.Interfaces; 4 | using MediatR; 5 | using Microsoft.Extensions.Logging; 6 | using System.Diagnostics; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | public class PerformanceBehaviour
live_help Are you sure to delete the selected record?