├── .gitignore ├── Code ├── GraphQL │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── CommanderGQL.csproj │ ├── CommanderGQL.sln │ ├── Data │ │ └── AppDbContext.cs │ ├── GraphQL │ │ ├── Commands │ │ │ ├── AddCommandInput.cs │ │ │ ├── AddCommandInputType.cs │ │ │ ├── AddCommandPayload.cs │ │ │ ├── AddCommandPayloadType.cs │ │ │ └── CommandType.cs │ │ ├── Mutation.cs │ │ ├── Platforms │ │ │ ├── AddPlatformInput.cs │ │ │ ├── AddPlatformInputType.cs │ │ │ ├── AddPlatformPayload.cs │ │ │ ├── AddPlatformPayloadType.cs │ │ │ └── PlatformType.cs │ │ ├── Query.cs │ │ └── Subscription.cs │ ├── Migrations │ │ ├── 20210120093548_AddPlatformToDb.Designer.cs │ │ ├── 20210120093548_AddPlatformToDb.cs │ │ ├── 20210123051644_AddCommandToDB.Designer.cs │ │ ├── 20210123051644_AddCommandToDB.cs │ │ └── AppDbContextModelSnapshot.cs │ ├── Models │ │ ├── Command.cs │ │ └── Platform.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── OData │ ├── Controllers │ │ └── WeatherForecastController.cs │ ├── OData.csproj │ ├── OData.sln │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── Startup.cs │ ├── WeatherForecast.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── REST │ ├── REST.sln │ ├── RESTClient │ │ ├── Program.cs │ │ └── RESTClient.csproj │ ├── RESTCommon │ │ ├── RESTCommon.csproj │ │ └── WeatherForecast.cs │ └── RESTServer │ │ ├── Controllers │ │ └── WeatherForecastController.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── RESTServer.csproj │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json ├── Refit │ ├── Refit.sln │ ├── RefitClient │ │ ├── IWeatherForecastService.cs │ │ ├── Program.cs │ │ └── RefitClient.csproj │ ├── RefitCommon │ │ ├── RefitCommon.csproj │ │ └── WeatherForecast.cs │ └── RefitServer │ │ ├── Controllers │ │ └── WeatherForecastController.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── RefitServer.csproj │ │ ├── Startup.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json ├── Sample Application │ ├── .aspnet │ │ ├── admin │ │ │ └── DataProtection-Keys │ │ │ │ ├── key-678ef51e-9fc4-4a0e-83fe-76723c365c3e.xml │ │ │ │ └── key-923f9357-f2a4-427c-af4b-84d001cfafef.xml │ │ ├── dealers │ │ │ └── DataProtection-Keys │ │ │ │ ├── key-2e349f1f-21c0-4eee-9c75-c5f92b871a70.xml │ │ │ │ └── key-9dbe0cd4-9f93-44db-b979-5592c45b0483.xml │ │ ├── identity │ │ │ └── DataProtection-Keys │ │ │ │ ├── key-3647e96a-6cfd-4ef6-af49-ae765593c45c.xml │ │ │ │ └── key-d899448a-f876-4b56-b8f0-1684a22c8d0d.xml │ │ ├── notifications │ │ │ └── DataProtection-Keys │ │ │ │ ├── key-385a953d-da17-4db5-b920-687d8ca92b40.xml │ │ │ │ └── key-611864fb-7266-443f-be99-6a690691950f.xml │ │ └── statistics │ │ │ └── DataProtection-Keys │ │ │ ├── key-44362846-b760-4ba1-8d84-10c9834fe0f1.xml │ │ │ └── key-a3e6a739-995a-4fd0-9a56-f4bb9134b168.xml │ ├── Client │ │ ├── .dockerignore │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── angular.json │ │ ├── browserslist │ │ ├── e2e │ │ │ ├── protractor.conf.js │ │ │ ├── src │ │ │ │ ├── app.e2e-spec.ts │ │ │ │ └── app.po.ts │ │ │ └── tsconfig.json │ │ ├── karma.conf.js │ │ ├── nginx.conf │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ │ ├── app │ │ │ │ ├── app-routing.module.ts │ │ │ │ ├── app.component.css │ │ │ │ ├── app.component.html │ │ │ │ ├── app.component.spec.ts │ │ │ │ ├── app.component.ts │ │ │ │ ├── app.module.ts │ │ │ │ ├── authentication │ │ │ │ │ ├── authentication-routing.module.ts │ │ │ │ │ ├── authentication.module.ts │ │ │ │ │ ├── authentication.service.ts │ │ │ │ │ ├── login │ │ │ │ │ │ ├── login.component.css │ │ │ │ │ │ ├── login.component.html │ │ │ │ │ │ ├── login.component.ts │ │ │ │ │ │ └── login.model.ts │ │ │ │ │ └── register │ │ │ │ │ │ ├── register.component.css │ │ │ │ │ │ ├── register.component.html │ │ │ │ │ │ ├── register.component.ts │ │ │ │ │ │ └── register.model.ts │ │ │ │ ├── cars │ │ │ │ │ ├── cars-routing.module.ts │ │ │ │ │ ├── cars.model.ts │ │ │ │ │ ├── cars.module.ts │ │ │ │ │ ├── cars.service.ts │ │ │ │ │ ├── category.model.ts │ │ │ │ │ ├── create │ │ │ │ │ │ ├── create.component.css │ │ │ │ │ │ ├── create.component.html │ │ │ │ │ │ └── create.component.ts │ │ │ │ │ ├── dealer-cars │ │ │ │ │ │ ├── dealer-cars.component.css │ │ │ │ │ │ ├── dealer-cars.component.html │ │ │ │ │ │ └── dealer-cars.component.ts │ │ │ │ │ ├── edit │ │ │ │ │ │ ├── edit.component.css │ │ │ │ │ │ ├── edit.component.html │ │ │ │ │ │ └── edit.component.ts │ │ │ │ │ ├── list │ │ │ │ │ │ ├── list.component.css │ │ │ │ │ │ ├── list.component.html │ │ │ │ │ │ └── list.component.ts │ │ │ │ │ ├── search │ │ │ │ │ │ ├── search.component.css │ │ │ │ │ │ ├── search.component.html │ │ │ │ │ │ ├── search.component.ts │ │ │ │ │ │ └── search.model.ts │ │ │ │ │ ├── sort │ │ │ │ │ │ ├── sort.component.css │ │ │ │ │ │ ├── sort.component.html │ │ │ │ │ │ ├── sort.component.ts │ │ │ │ │ │ └── sort.model.ts │ │ │ │ │ └── view │ │ │ │ │ │ ├── view.component.css │ │ │ │ │ │ ├── view.component.html │ │ │ │ │ │ └── view.component.ts │ │ │ │ ├── dealers │ │ │ │ │ ├── dealers-routing.module.ts │ │ │ │ │ ├── dealers.module.ts │ │ │ │ │ └── profile │ │ │ │ │ │ ├── password.model.ts │ │ │ │ │ │ ├── profile.component.css │ │ │ │ │ │ ├── profile.component.html │ │ │ │ │ │ ├── profile.component.ts │ │ │ │ │ │ ├── profile.model.ts │ │ │ │ │ │ └── profile.service.ts │ │ │ │ └── shared │ │ │ │ │ ├── auth-guard.service.ts │ │ │ │ │ ├── error-interceptor.service.ts │ │ │ │ │ ├── home │ │ │ │ │ ├── home.component.css │ │ │ │ │ ├── home.component.html │ │ │ │ │ └── home.component.ts │ │ │ │ │ ├── interceptor.service.ts │ │ │ │ │ ├── navbar │ │ │ │ │ ├── navbar.component.css │ │ │ │ │ ├── navbar.component.html │ │ │ │ │ └── navbar.component.ts │ │ │ │ │ ├── notifications.service.ts │ │ │ │ │ ├── pagination │ │ │ │ │ ├── pagination.component.css │ │ │ │ │ ├── pagination.component.html │ │ │ │ │ └── pagination.component.ts │ │ │ │ │ ├── pop-up │ │ │ │ │ ├── animations.ts │ │ │ │ │ ├── pop-up.component.css │ │ │ │ │ ├── pop-up.component.html │ │ │ │ │ ├── pop-up.component.spec.ts │ │ │ │ │ └── pop-up.component.ts │ │ │ │ │ ├── rouer-ext.service.ts │ │ │ │ │ ├── shared-routing.module.ts │ │ │ │ │ ├── shared.module.ts │ │ │ │ │ └── statistics │ │ │ │ │ ├── statistics.model.ts │ │ │ │ │ └── statistics.service.ts │ │ │ ├── assets │ │ │ │ ├── .gitkeep │ │ │ │ ├── 1.jpg │ │ │ │ ├── 2.jpg │ │ │ │ ├── 3.jpg │ │ │ │ ├── M8.jpg │ │ │ │ ├── RS7.jpg │ │ │ │ └── S63.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 │ ├── README.md │ ├── Server │ │ ├── .dockerignore │ │ ├── CarRentalSystem.Admin │ │ │ ├── CarRentalSystem.Admin.csproj │ │ │ ├── Controllers │ │ │ │ ├── AdministrationController.cs │ │ │ │ ├── DealersController.cs │ │ │ │ ├── HomeController.cs │ │ │ │ ├── IdentityController.cs │ │ │ │ └── StatisticsController.cs │ │ │ ├── Dockerfile │ │ │ ├── Infrastructure │ │ │ │ ├── JwtCookieAuthenticationMiddleware.cs │ │ │ │ └── ServiceCollectionExtensions.cs │ │ │ ├── Models │ │ │ │ ├── Dealers │ │ │ │ │ ├── DealerDetailsOutputModel.cs │ │ │ │ │ ├── DealerFormModel.cs │ │ │ │ │ └── DealerInputModel.cs │ │ │ │ ├── ErrorViewModel.cs │ │ │ │ ├── Identity │ │ │ │ │ ├── LoginFormModel.cs │ │ │ │ │ ├── UserInputModel.cs │ │ │ │ │ └── UserOutputModel.cs │ │ │ │ └── Statistics │ │ │ │ │ └── StatisticsOutputModel.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Services │ │ │ │ ├── Dealers │ │ │ │ │ └── IDealersService.cs │ │ │ │ ├── Identity │ │ │ │ │ └── IIdentityService.cs │ │ │ │ ├── ServiceEndpoints.cs │ │ │ │ └── Statistics │ │ │ │ │ └── IStatisticsService.cs │ │ │ ├── Startup.cs │ │ │ ├── Views │ │ │ │ ├── Dealers │ │ │ │ │ ├── Edit.cshtml │ │ │ │ │ └── Index.cshtml │ │ │ │ ├── Home │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Privacy.cshtml │ │ │ │ ├── Shared │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ └── _ValidationScriptsPartial.cshtml │ │ │ │ ├── Statistics │ │ │ │ │ └── Index.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ └── wwwroot │ │ │ │ ├── css │ │ │ │ └── site.css │ │ │ │ ├── favicon.ico │ │ │ │ ├── js │ │ │ │ └── site.js │ │ │ │ └── lib │ │ │ │ ├── bootstrap │ │ │ │ ├── LICENSE │ │ │ │ └── dist │ │ │ │ │ ├── css │ │ │ │ │ ├── bootstrap-grid.css │ │ │ │ │ ├── bootstrap-grid.css.map │ │ │ │ │ ├── bootstrap-grid.min.css │ │ │ │ │ ├── bootstrap-grid.min.css.map │ │ │ │ │ ├── bootstrap-reboot.css │ │ │ │ │ ├── bootstrap-reboot.css.map │ │ │ │ │ ├── bootstrap-reboot.min.css │ │ │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ │ │ ├── bootstrap.css │ │ │ │ │ ├── bootstrap.css.map │ │ │ │ │ ├── bootstrap.min.css │ │ │ │ │ └── bootstrap.min.css.map │ │ │ │ │ └── js │ │ │ │ │ ├── bootstrap.bundle.js │ │ │ │ │ ├── bootstrap.bundle.js.map │ │ │ │ │ ├── bootstrap.bundle.min.js │ │ │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ │ │ ├── bootstrap.js │ │ │ │ │ ├── bootstrap.js.map │ │ │ │ │ ├── bootstrap.min.js │ │ │ │ │ └── bootstrap.min.js.map │ │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── jquery.validate.unobtrusive.js │ │ │ │ └── jquery.validate.unobtrusive.min.js │ │ │ │ ├── jquery-validation │ │ │ │ ├── LICENSE.md │ │ │ │ └── dist │ │ │ │ │ ├── additional-methods.js │ │ │ │ │ ├── additional-methods.min.js │ │ │ │ │ ├── jquery.validate.js │ │ │ │ │ └── jquery.validate.min.js │ │ │ │ └── jquery │ │ │ │ ├── LICENSE.txt │ │ │ │ └── dist │ │ │ │ ├── jquery.js │ │ │ │ ├── jquery.min.js │ │ │ │ └── jquery.min.map │ │ ├── CarRentalSystem.Dealers │ │ │ ├── CarRentalSystem.Dealers.csproj │ │ │ ├── Controllers │ │ │ │ ├── CarAdsController.cs │ │ │ │ └── DealersController.cs │ │ │ ├── Data │ │ │ │ ├── Configurations │ │ │ │ │ ├── CarAdConfiguration.cs │ │ │ │ │ ├── CategoryConfiguration.cs │ │ │ │ │ ├── DealerConfiguration.cs │ │ │ │ │ └── ManufacturerConfiguration.cs │ │ │ │ ├── DataConstants.cs │ │ │ │ ├── DealersDataSeeder.cs │ │ │ │ ├── DealersDbContext.cs │ │ │ │ ├── Migrations │ │ │ │ │ ├── 20200612184927_InitialDealersTables.Designer.cs │ │ │ │ │ ├── 20200612184927_InitialDealersTables.cs │ │ │ │ │ ├── 20200618110013_LocationColumn.Designer.cs │ │ │ │ │ ├── 20200618110013_LocationColumn.cs │ │ │ │ │ ├── 20200710164708_MessagesTable.Designer.cs │ │ │ │ │ ├── 20200710164708_MessagesTable.cs │ │ │ │ │ └── DealersDbContextModelSnapshot.cs │ │ │ │ └── Models │ │ │ │ │ ├── CarAd.cs │ │ │ │ │ ├── Category.cs │ │ │ │ │ ├── Dealer.cs │ │ │ │ │ ├── Manufacturer.cs │ │ │ │ │ ├── Options.cs │ │ │ │ │ └── TransmissionType.cs │ │ │ ├── Dockerfile │ │ │ ├── Models │ │ │ │ ├── CarAds │ │ │ │ │ ├── CarAdDetailsOutputModel.cs │ │ │ │ │ ├── CarAdInputModel.cs │ │ │ │ │ ├── CarAdOutputModel.cs │ │ │ │ │ ├── CarAdsOutputModel.cs │ │ │ │ │ ├── CarAdsQuery.cs │ │ │ │ │ ├── CreateCarAdOutputModel.cs │ │ │ │ │ ├── MineCarAdOutputModel.cs │ │ │ │ │ ├── MineCarAdsOutputModel.cs │ │ │ │ │ └── SearchCarAdsOutputModel.cs │ │ │ │ ├── Categories │ │ │ │ │ └── CategoryOutputModel.cs │ │ │ │ └── Dealers │ │ │ │ │ ├── CreateDealerInputModel.cs │ │ │ │ │ ├── DealerDetailsOutputModel.cs │ │ │ │ │ ├── DealerOutputModel.cs │ │ │ │ │ └── EditDealerInputModel.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Services │ │ │ │ ├── CarAds │ │ │ │ │ ├── CarAdService.cs │ │ │ │ │ └── ICarAdService.cs │ │ │ │ ├── Categories │ │ │ │ │ ├── CategoryService.cs │ │ │ │ │ └── ICategoryService.cs │ │ │ │ ├── Dealers │ │ │ │ │ ├── DealerService.cs │ │ │ │ │ └── IDealerService.cs │ │ │ │ └── Manufacturers │ │ │ │ │ ├── IManufacturerService.cs │ │ │ │ │ └── ManufacturerService.cs │ │ │ ├── Startup.cs │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── CarRentalSystem.Identity │ │ │ ├── CarRentalSystem.Identity.csproj │ │ │ ├── Controllers │ │ │ │ └── IdentityController.cs │ │ │ ├── Data │ │ │ │ ├── DataConstants.cs │ │ │ │ ├── IdentityDataSeeder.cs │ │ │ │ ├── IdentityDbContext.cs │ │ │ │ ├── Migrations │ │ │ │ │ ├── 20200612184746_InitialIdentityTables.Designer.cs │ │ │ │ │ ├── 20200612184746_InitialIdentityTables.cs │ │ │ │ │ └── IdentityDbContextModelSnapshot.cs │ │ │ │ └── Models │ │ │ │ │ └── User.cs │ │ │ ├── Dockerfile │ │ │ ├── IdentitySettings.cs │ │ │ ├── Infrastructure │ │ │ │ └── ServiceCollectionExtensions.cs │ │ │ ├── Models │ │ │ │ └── Identity │ │ │ │ │ ├── ChangePasswordInputModel.cs │ │ │ │ │ ├── UserInputModel.cs │ │ │ │ │ └── UserOutputModel.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Services │ │ │ │ └── Identity │ │ │ │ │ ├── IIdentityService.cs │ │ │ │ │ ├── ITokenGeneratorService.cs │ │ │ │ │ ├── IdentityService.cs │ │ │ │ │ └── TokenGeneratorService.cs │ │ │ ├── Startup.cs │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── CarRentalSystem.Notifications │ │ │ ├── CarRentalSystem.Notifications.csproj │ │ │ ├── Constants.cs │ │ │ ├── Dockerfile │ │ │ ├── Hub │ │ │ │ └── NotificationsHub.cs │ │ │ ├── Infrastructure │ │ │ │ └── JwtConfiguration.cs │ │ │ ├── Messages │ │ │ │ └── CarAdCreatedConsumer.cs │ │ │ ├── NotificationSettings.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Startup.cs │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── CarRentalSystem.Schedule │ │ │ ├── CarRentalSystem.Schedule.csproj │ │ │ ├── Data │ │ │ │ ├── Configurations │ │ │ │ │ ├── DriverConfiguration.cs │ │ │ │ │ ├── FeedbackConfiguration.cs │ │ │ │ │ ├── RentedCarConfiguration.cs │ │ │ │ │ └── ReservationConfiguration.cs │ │ │ │ ├── Migrations │ │ │ │ │ ├── 20200625144831_InitialScheduleTables.Designer.cs │ │ │ │ │ ├── 20200625144831_InitialScheduleTables.cs │ │ │ │ │ └── ScheduleDbContextModelSnapshot.cs │ │ │ │ ├── Models │ │ │ │ │ ├── Driver.cs │ │ │ │ │ ├── Feedback.cs │ │ │ │ │ ├── RentedCar.cs │ │ │ │ │ └── Reservation.cs │ │ │ │ └── ScheduleDbContext.cs │ │ │ ├── Messages │ │ │ │ └── CarAdUpdatedConsumer.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Services │ │ │ │ ├── IRentedCarService.cs │ │ │ │ └── RentedCarService.cs │ │ │ ├── Startup.cs │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── CarRentalSystem.Statistics │ │ │ ├── CarRentalSystem.Statistics.csproj │ │ │ ├── Controllers │ │ │ │ ├── CarAdViewsController.cs │ │ │ │ └── StatisticsController.cs │ │ │ ├── Data │ │ │ │ ├── Configurations │ │ │ │ │ └── CarAdViewConfiguration.cs │ │ │ │ ├── Migrations │ │ │ │ │ ├── 20200618110313_InitialTables.Designer.cs │ │ │ │ │ ├── 20200618110313_InitialTables.cs │ │ │ │ │ ├── 20200618130926_CarAdViewIndex.Designer.cs │ │ │ │ │ ├── 20200618130926_CarAdViewIndex.cs │ │ │ │ │ ├── 20200917165120_MessagesTable.Designer.cs │ │ │ │ │ ├── 20200917165120_MessagesTable.cs │ │ │ │ │ └── StatisticsDbContextModelSnapshot.cs │ │ │ │ ├── Models │ │ │ │ │ ├── CarAdView.cs │ │ │ │ │ └── Statistics.cs │ │ │ │ ├── StatisticsDataSeeder.cs │ │ │ │ └── StatisticsDbContext.cs │ │ │ ├── Dockerfile │ │ │ ├── Messages │ │ │ │ └── CarAdCreatedConsumer.cs │ │ │ ├── Models │ │ │ │ ├── CarAdViews │ │ │ │ │ └── CarAdViewOutputModel.cs │ │ │ │ └── Statistics │ │ │ │ │ └── StatisticsOutputModel.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Services │ │ │ │ ├── CarAdViews │ │ │ │ │ ├── CarAdViewService.cs │ │ │ │ │ └── ICarAdViewService.cs │ │ │ │ └── Statistics │ │ │ │ │ ├── IStatisticsService.cs │ │ │ │ │ └── StatisticsService.cs │ │ │ ├── Startup.cs │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── CarRentalSystem.Watchdog │ │ │ ├── CarRentalSystem.Watchdog.csproj │ │ │ ├── Dockerfile │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── Startup.cs │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── CarRentalSystem.sln │ │ └── CarRentalSystem │ │ │ ├── ApplicationSettings.cs │ │ │ ├── CarRentalSystem.csproj │ │ │ ├── Common.env │ │ │ ├── Constants.cs │ │ │ ├── Controllers │ │ │ └── ApiController.cs │ │ │ ├── Data │ │ │ ├── Configuration │ │ │ │ └── MessageConfiguration.cs │ │ │ ├── DataConstants.cs │ │ │ ├── DataSeederConstants.cs │ │ │ ├── MessageDbContext.cs │ │ │ └── Models │ │ │ │ └── Message.cs │ │ │ ├── Infrastructure │ │ │ ├── ApplicationBuilderExtensions.cs │ │ │ ├── AuthorizeAdministratorAttribute.cs │ │ │ ├── ClaimsPrincipalExtensions.cs │ │ │ ├── ConfigurationExtensions.cs │ │ │ ├── EndpointRouteBuilderExtensions.cs │ │ │ ├── EnumerableExtensions.cs │ │ │ ├── HttpClientBuilderExtensions.cs │ │ │ ├── InfrastructureConstants.cs │ │ │ ├── JwtHeaderAuthenticationMiddleware.cs │ │ │ └── ServiceCollectionExtensions.cs │ │ │ ├── MessageQueueSettings.cs │ │ │ ├── Messages │ │ │ ├── Dealers │ │ │ │ ├── CarAdCreatedMessage.cs │ │ │ │ └── CarAdUpdatedMessage.cs │ │ │ └── MessagesHostedService.cs │ │ │ ├── Models │ │ │ ├── IMapFrom.cs │ │ │ └── MappingProfile.cs │ │ │ └── Services │ │ │ ├── DataService.cs │ │ │ ├── IDataSeeder.cs │ │ │ ├── IDataService.cs │ │ │ ├── Identity │ │ │ ├── CurrentTokenService.cs │ │ │ ├── CurrentUserService.cs │ │ │ ├── ICurrentTokenService.cs │ │ │ └── ICurrentUserService.cs │ │ │ └── Result.cs │ └── docker-compose.yml └── gRPC │ ├── gRPC.sln │ ├── gRPCClient │ ├── Program.cs │ └── gRPCClient.csproj │ └── gRPCServer │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Protos │ └── weather.proto │ ├── Services │ └── WeatherService.cs │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── gRPCServer.csproj ├── LICENSE ├── Presentation.pptx └── README.md /Code/GraphQL/CommanderGQL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | all 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Code/GraphQL/CommanderGQL.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31702.278 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommanderGQL", "CommanderGQL.csproj", "{ABFBECE5-11FE-476D-9514-DACD45FFC9BE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {ABFBECE5-11FE-476D-9514-DACD45FFC9BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {ABFBECE5-11FE-476D-9514-DACD45FFC9BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {ABFBECE5-11FE-476D-9514-DACD45FFC9BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {ABFBECE5-11FE-476D-9514-DACD45FFC9BE}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {C8E35444-1140-43D8-A85F-6F58F6828558} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Code/GraphQL/Data/AppDbContext.cs: -------------------------------------------------------------------------------- 1 | using CommanderGQL.Models; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace CommanderGQL.Data 5 | { 6 | public class AppDbContext : DbContext 7 | { 8 | public AppDbContext(DbContextOptions options) :base(options) 9 | { 10 | 11 | } 12 | 13 | public DbSet Platforms { get; set; } 14 | public DbSet Commands { get; set; } 15 | 16 | protected override void OnModelCreating(ModelBuilder modelBuilder) 17 | { 18 | modelBuilder 19 | .Entity() 20 | .HasMany(p => p.Commands) 21 | .WithOne(p => p.Platform!) 22 | .HasForeignKey(p => p.PlatformId); 23 | 24 | modelBuilder 25 | .Entity() 26 | .HasOne(p => p.Platform) 27 | .WithMany(p => p.Commands) 28 | .HasForeignKey(p => p.PlatformId); 29 | } 30 | 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Commands/AddCommandInput.cs: -------------------------------------------------------------------------------- 1 | namespace CommanderGQL.GraphQL.Commands 2 | { 3 | public record AddCommandInput(string HowTo, string CommandLine, int PlatformId); 4 | } -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Commands/AddCommandInputType.cs: -------------------------------------------------------------------------------- 1 | using HotChocolate.Types; 2 | 3 | namespace CommanderGQL.GraphQL.Commands 4 | { 5 | public class AddCommandInputType : InputObjectType 6 | { 7 | protected override void Configure(IInputObjectTypeDescriptor descriptor) 8 | { 9 | descriptor.Description("Represents the input to add for a command."); 10 | 11 | descriptor 12 | .Field(c => c.HowTo) 13 | .Description("Represents the how-to for the command."); 14 | descriptor 15 | .Field(c => c.CommandLine) 16 | .Description("Represents the command line."); 17 | descriptor 18 | .Field(c => c.PlatformId) 19 | .Description("Represents the unique ID of the platform which the command belongs."); 20 | 21 | base.Configure(descriptor); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Commands/AddCommandPayload.cs: -------------------------------------------------------------------------------- 1 | using CommanderGQL.Models; 2 | 3 | namespace CommanderGQL.GraphQL.Commands 4 | { 5 | public record AddCommandPayload(Command command); 6 | } -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Commands/AddCommandPayloadType.cs: -------------------------------------------------------------------------------- 1 | using HotChocolate.Types; 2 | 3 | namespace CommanderGQL.GraphQL.Commands 4 | { 5 | public class AddCommandPayloadType : ObjectType 6 | { 7 | protected override void Configure(IObjectTypeDescriptor descriptor) 8 | { 9 | descriptor.Description("Represents the payload to return for an added command."); 10 | 11 | descriptor 12 | .Field(c => c.command) 13 | .Description("Represents the added command."); 14 | 15 | base.Configure(descriptor); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Platforms/AddPlatformInput.cs: -------------------------------------------------------------------------------- 1 | namespace CommanderGQL.GraphQL.Platforms 2 | { 3 | public record AddPlatformInput(string Name); 4 | } -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Platforms/AddPlatformInputType.cs: -------------------------------------------------------------------------------- 1 | using HotChocolate.Types; 2 | 3 | namespace CommanderGQL.GraphQL.Platforms 4 | { 5 | public class AddPlatformInputType : InputObjectType 6 | { 7 | protected override void Configure(IInputObjectTypeDescriptor descriptor) 8 | { 9 | descriptor.Description("Represents the input to add for a platform."); 10 | 11 | descriptor 12 | .Field(p => p.Name) 13 | .Description("Represents the name for the platform."); 14 | 15 | base.Configure(descriptor); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Platforms/AddPlatformPayload.cs: -------------------------------------------------------------------------------- 1 | using CommanderGQL.Models; 2 | 3 | namespace CommanderGQL.GraphQL.Platforms 4 | { 5 | public record AddPlatformPayload(Platform platform); 6 | } -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Platforms/AddPlatformPayloadType.cs: -------------------------------------------------------------------------------- 1 | using HotChocolate.Types; 2 | 3 | namespace CommanderGQL.GraphQL.Platforms 4 | { 5 | public class AddPlatformPayloadType : ObjectType 6 | { 7 | protected override void Configure(IObjectTypeDescriptor descriptor) 8 | { 9 | descriptor.Description("Represents the payload to return for an added platform."); 10 | 11 | descriptor 12 | .Field(p => p.platform) 13 | .Description("Represents the added platform."); 14 | 15 | base.Configure(descriptor); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/GraphQL/GraphQL/Subscription.cs: -------------------------------------------------------------------------------- 1 | using CommanderGQL.Models; 2 | using HotChocolate; 3 | using HotChocolate.Types; 4 | 5 | namespace CommanderGQL.GraphQL 6 | { 7 | /// 8 | /// Represents the subscriptions available. 9 | /// 10 | [GraphQLDescription("Represents the queries available.")] 11 | public class Subscription 12 | { 13 | /// 14 | /// The subscription for added . 15 | /// 16 | /// The . 17 | /// The added . 18 | [Subscribe] 19 | [Topic] 20 | [GraphQLDescription("The subscription for added platform.")] 21 | public Platform OnPlatformAdded([EventMessage] Platform platform) 22 | { 23 | return platform; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Code/GraphQL/Migrations/20210120093548_AddPlatformToDb.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace CommanderGQL.Migrations 4 | { 5 | public partial class AddPlatformToDb : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.CreateTable( 10 | name: "Platforms", 11 | columns: table => new 12 | { 13 | Id = table.Column(type: "int", nullable: false) 14 | .Annotation("SqlServer:Identity", "1, 1"), 15 | Name = table.Column(type: "nvarchar(max)", nullable: false), 16 | LicenseKey = table.Column(type: "nvarchar(max)", nullable: true) 17 | }, 18 | constraints: table => 19 | { 20 | table.PrimaryKey("PK_Platforms", x => x.Id); 21 | }); 22 | } 23 | 24 | protected override void Down(MigrationBuilder migrationBuilder) 25 | { 26 | migrationBuilder.DropTable( 27 | name: "Platforms"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Code/GraphQL/Models/Command.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace CommanderGQL.Models 4 | { 5 | /// 6 | /// Represents any executable command. 7 | /// 8 | public class Command 9 | { 10 | /// 11 | /// Represents the unique ID for the command. 12 | /// 13 | [Key] 14 | public int Id { get; set; } 15 | 16 | /// 17 | /// Represents the how-to for the command. 18 | /// 19 | [Required] 20 | public string HowTo { get; set; } 21 | 22 | /// 23 | /// Represents the command line. 24 | /// 25 | [Required] 26 | public string CommandLine { get; set; } 27 | 28 | /// 29 | /// Represents the unique ID of the platform which the command belongs. 30 | /// 31 | [Required] 32 | public int PlatformId { get; set; } 33 | 34 | /// 35 | /// This is the platform to which the command belongs. 36 | /// 37 | public Platform Platform { get; set; } 38 | } 39 | } -------------------------------------------------------------------------------- /Code/GraphQL/Models/Platform.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace CommanderGQL.Models 5 | { 6 | /// 7 | /// Represents any software or service that has a command line interface. 8 | /// 9 | public class Platform 10 | { 11 | /// 12 | /// Represents the unique ID for the platform. 13 | /// 14 | [Key] 15 | public int Id { get; set; } 16 | 17 | /// 18 | /// Represents the name for the platform. 19 | /// 20 | [Required] 21 | public string Name { get; set; } 22 | 23 | /// 24 | /// Represents a purchased, valid license for the platform. 25 | /// 26 | public string LicenseKey { get; set; } 27 | 28 | /// 29 | /// This is the list of available commands for this platform. 30 | /// 31 | public ICollection Commands { get; set; } = new List(); 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /Code/GraphQL/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace CommanderGQL 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 | -------------------------------------------------------------------------------- /Code/GraphQL/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:50760", 7 | "sslPort": 44379 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "CommanderGQL": { 19 | "commandName": "Project", 20 | "dotnetRunMessages": "true", 21 | "launchBrowser": true, 22 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Code/GraphQL/README.md: -------------------------------------------------------------------------------- 1 | Addapted from: https://github.com/binarythistle/S04E01---.NET-5-GraphQL-API 2 | 3 | Watch the tutorial here: https://www.youtube.com/watch?v=HuN94qNwQmM -------------------------------------------------------------------------------- /Code/GraphQL/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "ConnectionStrings": { 10 | "CommandConStr" : "Server=.;Database=CommandsDB;Integrated Security=True" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/GraphQL/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Code/OData/OData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Code/OData/OData.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31213.239 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OData", "OData.csproj", "{274499EC-338C-4FAB-8C15-B1FB79579284}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {274499EC-338C-4FAB-8C15-B1FB79579284}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {274499EC-338C-4FAB-8C15-B1FB79579284}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {274499EC-338C-4FAB-8C15-B1FB79579284}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {274499EC-338C-4FAB-8C15-B1FB79579284}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {C4687E61-2CFF-4183-9751-064BD89E2A4F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Code/OData/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace Odata 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 | -------------------------------------------------------------------------------- /Code/OData/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:12197", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "ODataNewtonsoftJsonSample": { 21 | "commandName": "Project", 22 | "dotnetRunMessages": "true", 23 | "launchBrowser": true, 24 | "launchUrl": "$odata", 25 | "applicationUrl": "http://localhost:5000", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Code/OData/README.md: -------------------------------------------------------------------------------- 1 | Addapted from: https://github.com/OData/AspNetCoreOData -------------------------------------------------------------------------------- /Code/OData/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Odata 5 | { 6 | public class WeatherForecast 7 | { 8 | [Key] 9 | public int Key { get; set; } 10 | 11 | public DateTime Date { get; set; } 12 | 13 | public int TemperatureC { get; set; } 14 | 15 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 16 | 17 | public string Summary { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Code/OData/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/OData/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Code/REST/RESTClient/RESTClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Code/REST/RESTCommon/RESTCommon.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Code/REST/RESTCommon/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RESTCommon 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/REST/RESTServer/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | namespace RestServer.Controllers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using Microsoft.AspNetCore.Mvc; 7 | using RESTCommon; 8 | 9 | [ApiController] 10 | [Route("[controller]")] 11 | public class WeatherForecastController : ControllerBase 12 | { 13 | private static readonly List Data = new List 14 | { 15 | new WeatherForecast { Summary = "Cold" } 16 | }; 17 | 18 | [HttpGet] 19 | public WeatherForecast Get() => Data.OrderBy(x => Guid.NewGuid()).FirstOrDefault(); 20 | 21 | [HttpPost] 22 | public IActionResult Post(WeatherForecast weather) 23 | { 24 | if (string.IsNullOrEmpty(weather.Summary)) 25 | { 26 | return this.BadRequest(); 27 | } 28 | 29 | Data.Add(weather); 30 | 31 | return this.Ok(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Code/REST/RESTServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace RestServer 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 | -------------------------------------------------------------------------------- /Code/REST/RESTServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:58436", 8 | "sslPort": 44322 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "RestServer": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "weatherforecast", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Code/REST/RESTServer/RESTServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Code/REST/RESTServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/REST/RESTServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Code/Refit/RefitClient/IWeatherForecastService.cs: -------------------------------------------------------------------------------- 1 | namespace RefitClient 2 | { 3 | using System.Threading.Tasks; 4 | using Refit; 5 | using RefitCommon; 6 | 7 | public interface IWeatherForecastService 8 | { 9 | [Post("/WeatherForecast")] 10 | Task CreateWeatherForecast([Body] WeatherForecast forecast); 11 | 12 | [Get("/WeatherForecast")] 13 | Task GetWeatherForecast(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/Refit/RefitClient/Program.cs: -------------------------------------------------------------------------------- 1 | namespace RefitClient 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | using Refit; 6 | using RefitCommon; 7 | 8 | public class Program 9 | { 10 | public static async Task Main() 11 | { 12 | var service = RestService.For("https://localhost:5001"); 13 | 14 | // Save weather 15 | var weather = new WeatherForecast 16 | { 17 | Summary = "Hot" 18 | }; 19 | 20 | await service.CreateWeatherForecast(weather); 21 | 22 | // Get weather 23 | var result = await service.GetWeatherForecast(); 24 | 25 | Console.WriteLine(result.Summary); 26 | 27 | Console.ReadLine(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Code/Refit/RefitClient/RefitClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Code/Refit/RefitCommon/RefitCommon.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Code/Refit/RefitCommon/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RefitCommon 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/Refit/RefitServer/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | namespace RefitServer.Controllers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using Microsoft.AspNetCore.Mvc; 7 | using RefitCommon; 8 | 9 | [ApiController] 10 | [Route("[controller]")] 11 | public class WeatherForecastController : ControllerBase 12 | { 13 | private static readonly List Data = new List 14 | { 15 | new WeatherForecast { Summary = "Cold" } 16 | }; 17 | 18 | [HttpGet] 19 | public WeatherForecast Get() => Data.OrderBy(x => Guid.NewGuid()).FirstOrDefault(); 20 | 21 | [HttpPost] 22 | public IActionResult Post(WeatherForecast weather) 23 | { 24 | if (string.IsNullOrEmpty(weather.Summary)) 25 | { 26 | return this.BadRequest(); 27 | } 28 | 29 | Data.Add(weather); 30 | 31 | return this.Ok(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Code/Refit/RefitServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace RefitServer 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 | -------------------------------------------------------------------------------- /Code/Refit/RefitServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:59330", 8 | "sslPort": 44303 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "RefitServer": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "weatherforecast", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Code/Refit/RefitServer/RefitServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Code/Refit/RefitServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Refit/RefitServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/admin/DataProtection-Keys/key-678ef51e-9fc4-4a0e-83fe-76723c365c3e.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2020-09-18T10:20:43.7742142Z 4 | 2020-09-18T10:20:43.7617421Z 5 | 2020-12-17T10:20:43.7617421Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | KgwGmOnH4GUo57rpIHs/6NizHF05AcY0gp/xnYWZNHA41AsF5Pyse1DO5enVkVaPjkjeZ9ghvlSqRd44jrqr0w== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/admin/DataProtection-Keys/key-923f9357-f2a4-427c-af4b-84d001cfafef.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2021-10-07T10:37:12.1197226Z 4 | 2021-10-07T10:37:12.0704596Z 5 | 2022-01-05T10:37:12.0704596Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | 5uLl/d1wvc2vMqVzO40ax1UmAHq6uajHLBIagslnR0RDgkU/5bxllo1ApbB2PcHcXXOlWttQN1PDiWRsMc7Gzw== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/dealers/DataProtection-Keys/key-2e349f1f-21c0-4eee-9c75-c5f92b871a70.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2020-09-18T10:20:59.7291542Z 4 | 2020-09-18T10:20:59.7233102Z 5 | 2020-12-17T10:20:59.7233102Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | i+u3rsZe0S/SCLnJSPHUNEpPmiOdIBCxxiQxRIy64Q+gan166crkqFtwVsYmbbeAJdEZDm9/kGak0b9vW6l6yA== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/dealers/DataProtection-Keys/key-9dbe0cd4-9f93-44db-b979-5592c45b0483.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2021-10-07T10:37:26.1308317Z 4 | 2021-10-07T10:37:26.0961177Z 5 | 2022-01-05T10:37:26.0961177Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | mmjHn/kzPOLMK6r1HXSsrnERYJOvwi5z9mkXJ83/5CRSmWLJPX515XbVbFZzS6w3XFdaNLjFDfa4bv4+gEznew== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/identity/DataProtection-Keys/key-3647e96a-6cfd-4ef6-af49-ae765593c45c.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2020-09-18T10:20:45.3209622Z 4 | 2020-09-18T10:20:45.3140744Z 5 | 2020-12-17T10:20:45.3140744Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | E6IKTme4turIG22iPt7O9pk64xYeEtbpbA0b6NLoLBdJi0aPI3aTjKbblb4CKysaJ/b544wuZto6ifBxnjKyuA== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/identity/DataProtection-Keys/key-d899448a-f876-4b56-b8f0-1684a22c8d0d.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2021-10-07T10:37:07.6313427Z 4 | 2021-10-07T10:37:07.5849155Z 5 | 2022-01-05T10:37:07.5849155Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | tIMu/jRJ7HfJDoS7wg8K+RDqJFjfuvd5g2AB34niWeEUFxMqp1TaW2CsqExGMuhY6lZBA8WsHD9xojNyoOWF4g== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/notifications/DataProtection-Keys/key-385a953d-da17-4db5-b920-687d8ca92b40.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2021-10-07T10:37:08.3254969Z 4 | 2021-10-07T10:37:08.2771063Z 5 | 2022-01-05T10:37:08.2771063Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | +g7PH6JI1HB2ATkZ1yAofU/ffwHSCaD5eXllEF6uaypipcn4Nn5X0ej4ZiFvNsJwhgH/XuCmXY0jcYaWnPRs4Q== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/notifications/DataProtection-Keys/key-611864fb-7266-443f-be99-6a690691950f.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2020-09-18T10:20:43.6180984Z 4 | 2020-09-18T10:20:43.6047592Z 5 | 2020-12-17T10:20:43.6047592Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | 7jy97k+zwZaHKdCiNBqrt/06sdUIhlh7TEmdKNWds2tdRhiLGaYio37+dgLbly3OcpL5x5N98ca2c05wPD/3Xg== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/statistics/DataProtection-Keys/key-44362846-b760-4ba1-8d84-10c9834fe0f1.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2021-10-07T10:37:28.4730575Z 4 | 2021-10-07T10:37:28.4373879Z 5 | 2022-01-05T10:37:28.4373879Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | R/MgB8rvZfEa66lHbpGCgwpHDuAFLyErBU3gAVUw2Se2i7Ei5sGfOdvi9Is+1Yh7Y0ZyKa5SMBFnd0wfnMeYHQ== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/.aspnet/statistics/DataProtection-Keys/key-a3e6a739-995a-4fd0-9a56-f4bb9134b168.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 2020-09-18T10:20:59.8230889Z 4 | 2020-09-18T10:20:59.8159882Z 5 | 2020-12-17T10:20:59.8159882Z 6 | 7 | 8 | 9 | 10 | 11 | 12 | RbC2Gel2AT/SfF/ATdvY/umexrMDj49J3fjVZM3sXNmw2Q0fdyLQV5eQrfPOyf+i8JpS4e47eHn01XUk8EssMg== 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/.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 -------------------------------------------------------------------------------- /Code/Sample Application/Client/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/.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 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/Dockerfile: -------------------------------------------------------------------------------- 1 | # base image 2 | FROM node:12.2.0 as build 3 | 4 | # set working directory 5 | WORKDIR /app 6 | 7 | # add `/app/node_modules/.bin` to $PATH 8 | ENV PATH /app/node_modules/.bin:$PATH 9 | 10 | # install and cache app dependencies 11 | COPY package.json /app/package.json 12 | RUN npm install 13 | RUN npm install -g @angular/cli@10.1.2 14 | 15 | # add app 16 | COPY . /app 17 | 18 | # generate build 19 | ARG configuration=production 20 | 21 | RUN ng build --output-path=dist --configuration=$configuration 22 | 23 | # base image 24 | FROM nginx:1.16.0-alpine 25 | 26 | # Remove default Nginx website 27 | RUN rm -rf /usr/share/nginx/html/* 28 | 29 | # Copy Nginx configuration 30 | COPY ./nginx.conf /etc/nginx/nginx.conf 31 | 32 | # copy artifact build from the 'build environment' 33 | COPY --from=build /app/dist /usr/share/nginx/html 34 | 35 | # expose port 80 36 | EXPOSE 80 37 | 38 | # run nginx 39 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /Code/Sample Application/Client/browserslist: -------------------------------------------------------------------------------- 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 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /Code/Sample Application/Client/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 } = 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 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /Code/Sample Application/Client/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('car-rental-system 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 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/car-rental-system'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { 4 | worker_connections 1024; 5 | } 6 | 7 | http { 8 | server { 9 | listen 80; 10 | server_name localhost; 11 | 12 | root /usr/share/nginx/html; 13 | index index.html index.htm; 14 | include /etc/nginx/mime.types; 15 | 16 | gzip on; 17 | gzip_min_length 1000; 18 | gzip_proxied expired no-cache no-store private auth; 19 | gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; 20 | 21 | location / { 22 | try_files $uri $uri/ /index.html; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | 5 | const routes: Routes = [ 6 | { 7 | path: 'auth', 8 | loadChildren: () => import('./authentication/authentication-routing.module').then(m => m.AuthenticationRoutingModule) 9 | }, 10 | { 11 | path: 'cars', 12 | loadChildren: () => import('./cars/cars-routing.module').then(m => m.CarsRoutingModule) 13 | }, 14 | { 15 | path: 'dealers', 16 | loadChildren: () => import('./dealers/dealers-routing.module').then(m => m.DealersRoutingModule) 17 | }, 18 | { 19 | path: '', 20 | loadChildren: () => import('./shared/shared-routing.module').then(m => m.SharedRoutingModule) 21 | }, 22 | ]; 23 | 24 | @NgModule({ 25 | imports: [ 26 | RouterModule.forRoot(routes) 27 | ], 28 | exports: [RouterModule] 29 | }) 30 | export class AppRoutingModule { } 31 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/app/app.component.css -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/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 = 'car-rental-system'; 10 | } 11 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { AuthenticationModule } from './authentication/authentication.module'; 7 | import { CarsModule } from './cars/cars.module'; 8 | import { SharedModule } from './shared/shared.module'; 9 | import { DealersModule } from './dealers/dealers.module' 10 | 11 | @NgModule({ 12 | declarations: [ 13 | AppComponent, 14 | ], 15 | imports: [ 16 | BrowserModule, 17 | AppRoutingModule, 18 | SharedModule, 19 | AuthenticationModule, 20 | CarsModule, 21 | DealersModule, 22 | ], 23 | providers: [], 24 | bootstrap: [AppComponent] 25 | }) 26 | export class AppModule { } 27 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/authentication/authentication-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { LoginComponent } from './login/login.component'; 4 | import { RegisterComponent } from './register/register.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', component: LoginComponent }, 8 | { path: 'register', component: RegisterComponent }, 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [ 13 | RouterModule.forChild(routes) 14 | ], 15 | exports: [RouterModule] 16 | }) 17 | export class AuthenticationRoutingModule { } 18 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/authentication/authentication.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { LoginComponent } from './login/login.component'; 4 | import { SharedModule } from '../shared/shared.module'; 5 | import { AuthenticationRoutingModule } from './authentication-routing.module'; 6 | import { RegisterComponent } from './register/register.component'; 7 | 8 | 9 | @NgModule({ 10 | declarations: [ 11 | LoginComponent, 12 | RegisterComponent, 13 | ], 14 | imports: [ 15 | CommonModule, 16 | SharedModule, 17 | AuthenticationRoutingModule, 18 | ], 19 | exports: [ 20 | LoginComponent, 21 | RegisterComponent, 22 | ] 23 | }) 24 | export class AuthenticationModule { } 25 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/authentication/login/login.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/app/authentication/login/login.component.css -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/authentication/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 |

Don't have an account? Sign Up

15 | 16 |
17 |
18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/authentication/login/login.model.ts: -------------------------------------------------------------------------------- 1 | export interface LoginFormModel { 2 | email: string; 3 | password: string; 4 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/authentication/register/register.component.css: -------------------------------------------------------------------------------- 1 | p a :hover { 2 | color: blue; 3 | border-bottom: 1px solid blue; 4 | cursor: pointer; 5 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/authentication/register/register.model.ts: -------------------------------------------------------------------------------- 1 | export interface RegisterModelForm { 2 | email: string; 3 | password: string; 4 | name: string; 5 | phoneNumber: string; 6 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/cars-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ListComponent } from './list/list.component'; 4 | import { CreateComponent } from './create/create.component'; 5 | import { EditComponent } from './edit/edit.component'; 6 | import { ViewComponent } from './view/view.component'; 7 | import { 8 | AuthGuardService as AuthGuard 9 | } from '../shared/auth-guard.service'; 10 | import { DealerCarsComponent } from './dealer-cars/dealer-cars.component'; 11 | 12 | const routes: Routes = [ 13 | { path: '', component: ListComponent }, 14 | { path:'mine', component: DealerCarsComponent, canActivate: [AuthGuard] }, 15 | { path: 'create', component: CreateComponent, canActivate: [AuthGuard] }, 16 | { path: ':id', component: ViewComponent }, 17 | { path: ':id/edit', component: EditComponent, canActivate: [AuthGuard] }, 18 | ]; 19 | 20 | @NgModule({ 21 | imports: [ 22 | RouterModule.forChild(routes) 23 | ], 24 | exports: [RouterModule] 25 | }) 26 | 27 | export class CarsRoutingModule { } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/cars.model.ts: -------------------------------------------------------------------------------- 1 | import { Profile } from '../dealers/profile/profile.model'; 2 | 3 | export interface Car { 4 | id?: number; 5 | manufacturer: string; 6 | model: string; 7 | category: number; 8 | imageUrl: string; 9 | pricePerDay: number; 10 | hasClimateControl: boolean; 11 | numberOfSeats: number; 12 | transmissionType: number; 13 | isAvailable?: boolean; 14 | dealer?: Profile; 15 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/cars.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ListComponent } from './list/list.component'; 4 | import { CreateComponent } from './create/create.component'; 5 | import { EditComponent } from './edit/edit.component'; 6 | import { ViewComponent } from './view/view.component'; 7 | import { CarsRoutingModule } from './cars-routing.module'; 8 | import { SharedModule } from '../shared/shared.module'; 9 | import { SearchComponent } from './search/search.component'; 10 | import { SortComponent } from './sort/sort.component'; 11 | import { DealerCarsComponent } from './dealer-cars/dealer-cars.component'; 12 | 13 | @NgModule({ 14 | declarations: [ListComponent, CreateComponent, EditComponent, ViewComponent, SearchComponent, SortComponent, DealerCarsComponent], 15 | imports: [ 16 | CommonModule, 17 | SharedModule, 18 | CarsRoutingModule, 19 | ], 20 | exports: [ListComponent, CreateComponent, EditComponent, ViewComponent, SearchComponent, DealerCarsComponent] 21 | }) 22 | export class CarsModule { } 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/category.model.ts: -------------------------------------------------------------------------------- 1 | export interface Category { 2 | id?: number; 3 | name: string; 4 | description: string; 5 | totalCarAds: number; 6 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/create/create.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/app/cars/create/create.component.css -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/dealer-cars/dealer-cars.component.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | margin-left: 1%; 3 | } 4 | 5 | .pop-up-box { 6 | overflow-y: auto; 7 | max-height: 100vh; 8 | margin: 0 auto; 9 | width: 100%; 10 | padding: 2rem; 11 | box-shadow: 6px 6px 0 rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12); 12 | background: white; 13 | color: #007bff; 14 | text-align: left; 15 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/edit/edit.component.css: -------------------------------------------------------------------------------- 1 | img { 2 | width: 100%; 3 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/list/list.component.css: -------------------------------------------------------------------------------- 1 | .card { 2 | width: 100%; 3 | margin: 10% 5%; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/list/list.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 |
7 |
8 |
9 |
10 |
11 | Car image 12 |
13 |
{{ c.manufacturer }}
14 |

${{ c.pricePerDay }}

15 | Details 16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | 28 |

No Cars Found

29 |
30 | 31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/list/list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { CarsService } from '../cars.service'; 3 | import { Car } from '../cars.model'; 4 | import { ActivatedRoute } from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-list', 8 | templateUrl: './list.component.html', 9 | styleUrls: ['./list.component.css'] 10 | }) 11 | export class ListComponent implements OnInit { 12 | cars: Array; 13 | id: string; 14 | category = null; 15 | constructor(private carsService: CarsService, private route: ActivatedRoute) { } 16 | 17 | ngOnInit(): void { 18 | this.fetchCars() 19 | 20 | } 21 | 22 | fetchCars() { 23 | this.carsService.getCars().subscribe(cars => { 24 | this.cars = cars['carAds']; 25 | }) 26 | } 27 | 28 | assignCars(event) { 29 | this.cars = event['carAds']; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/search/search.component.css: -------------------------------------------------------------------------------- 1 | label { 2 | margin-right: 2%; 3 | } 4 | 5 | input[number] { 6 | width: 50%; 7 | } 8 | 9 | form { 10 | margin: 0% 1%; 11 | border: none; 12 | border-right: 1px solid gray; 13 | padding-top: 0%; 14 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/search/search.model.ts: -------------------------------------------------------------------------------- 1 | export interface Search { 2 | manufacturer: string; 3 | dealer: string; 4 | category: number; 5 | minPricePerDay: number; 6 | maxPricePerDay: number; 7 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/sort/sort.component.css: -------------------------------------------------------------------------------- 1 | form { 2 | margin: 1%; 3 | border: none; 4 | padding: 5%; 5 | border-right: 1px solid gray; 6 | margin-bottom: 0%; 7 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/sort/sort.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 9 |
10 |
11 | 12 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/sort/sort.model.ts: -------------------------------------------------------------------------------- 1 | export interface Sort { 2 | sortBy: string; 3 | order: string; 4 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/view/view.component.css: -------------------------------------------------------------------------------- 1 | img { 2 | width: 100%; 3 | } 4 | 5 | h3 { 6 | display: inline-block; 7 | } 8 | label { 9 | margin-left: 5%; 10 | font-size: 1.5rem; 11 | } 12 | 13 | .container { 14 | margin-top: 2%; 15 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/view/view.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Car image 5 |
6 |
7 |

Manufacturer:

8 |

Model:

9 |

Category:

10 |

Price Per Day:

11 |

Climate Control:

12 |

Number of Seats:

13 |

Dealer Phone:

14 |

Dealer Name:

15 |

Dealer Phone:

16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/cars/view/view.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Car } from '../cars.model'; 3 | import { CarsService } from '../cars.service'; 4 | import { ActivatedRoute } from '@angular/router'; 5 | 6 | @Component({ 7 | selector: 'app-view', 8 | templateUrl: './view.component.html', 9 | styleUrls: ['./view.component.css'] 10 | }) 11 | export class ViewComponent implements OnInit { 12 | car: Car; 13 | id: string; 14 | constructor(private carService: CarsService, private route: ActivatedRoute) { 15 | this.id = this.route.snapshot.paramMap.get('id'); 16 | } 17 | 18 | ngOnInit(): void { 19 | this.carService.getCar(this.id).subscribe(car => { 20 | this.car = car; 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/dealers/dealers-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { ProfileComponent } from './profile/profile.component'; 4 | 5 | const routes: Routes = [ 6 | { path: 'me', component: ProfileComponent}, 7 | 8 | ]; 9 | 10 | @NgModule({ 11 | imports: [ 12 | RouterModule.forChild(routes) 13 | ], 14 | exports: [RouterModule] 15 | }) 16 | 17 | export class DealersRoutingModule { } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/dealers/dealers.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ProfileComponent } from './profile/profile.component'; 4 | import { SharedModule } from '../shared/shared.module'; 5 | import { DealersRoutingModule } from './dealers-routing.module'; 6 | import { ReactiveFormsModule } from '@angular/forms'; 7 | 8 | 9 | 10 | @NgModule({ 11 | declarations: [ProfileComponent], 12 | imports: [ 13 | CommonModule, 14 | SharedModule, 15 | DealersRoutingModule, 16 | ReactiveFormsModule, 17 | ], 18 | exports: [ProfileComponent] 19 | }) 20 | export class DealersModule { } 21 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/dealers/profile/password.model.ts: -------------------------------------------------------------------------------- 1 | export interface PasswordChange { 2 | currentPassword: string; 3 | newPassword: string; 4 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/dealers/profile/profile.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/app/dealers/profile/profile.component.css -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/dealers/profile/profile.model.ts: -------------------------------------------------------------------------------- 1 | export interface Profile { 2 | id?: number; 3 | name: string; 4 | phoneNumber: string; 5 | totalCarAds?: number; 6 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/dealers/profile/profile.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { environment } from 'src/environments/environment'; 4 | import { Observable } from 'rxjs'; 5 | import { Profile } from './profile.model'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class ProfileService { 11 | dealerPath: string = environment.dealersApiUrl + 'Dealers/' 12 | constructor(private http: HttpClient) { } 13 | 14 | getDealer(id): Observable { 15 | return this.http.get(this.dealerPath + id) 16 | } 17 | 18 | editDealer(id, payload): Observable { 19 | return this.http.put(this.dealerPath + id, payload) 20 | } 21 | 22 | changePassword(payload) { 23 | return this.http.put(environment.identityApiUrl + 'identity/changePassword', payload); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { CanActivate, Router } from '@angular/router'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class AuthGuardService implements CanActivate { 8 | 9 | constructor(private router: Router) { } 10 | 11 | canActivate(): boolean { 12 | if (!this.isAuthenticated()) { 13 | this.router.navigate(['auth']); 14 | return false; 15 | } 16 | return true; 17 | } 18 | 19 | isAuthenticated() { 20 | return localStorage.getItem('token') 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/home/home.component.css: -------------------------------------------------------------------------------- 1 | .container-fluid { 2 | padding: 0%; 3 | overflow: hidden; 4 | } 5 | 6 | 7 | .box { 8 | border: 1px solid grey; 9 | margin: 2%; 10 | position: relative; 11 | height: 32rem; 12 | padding: 5%; 13 | } 14 | 15 | .action { 16 | position: absolute; 17 | bottom: 0; 18 | left: 0; 19 | margin: 5%; 20 | } 21 | 22 | .btn-primary { 23 | background-color: #6c757d!important; 24 | } 25 | 26 | .container { 27 | margin-top: 5%; 28 | margin-bottom: 5%; 29 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Category } from 'src/app/cars/category.model'; 3 | import { CarsService } from 'src/app/cars/cars.service'; 4 | import { Router } from '@angular/router'; 5 | import { StatisticsService } from '../statistics/statistics.service'; 6 | import { Statistics } from '../statistics/statistics.model'; 7 | 8 | @Component({ 9 | selector: 'app-home', 10 | templateUrl: './home.component.html', 11 | styleUrls: ['./home.component.css'] 12 | }) 13 | export class HomeComponent implements OnInit { 14 | categories: Array; 15 | statistics: Statistics; 16 | 17 | constructor( 18 | private carsService: CarsService, 19 | private statisticsService: StatisticsService, 20 | private router: Router) { } 21 | 22 | ngOnInit(): void { 23 | this.carsService.getCategories().subscribe(res => { 24 | this.categories = res; 25 | }); 26 | 27 | this.statisticsService.getStatistics().subscribe(res => { 28 | this.statistics = res; 29 | }); 30 | } 31 | 32 | goToCars(id) { 33 | this.router.navigate(['cars'], { queryParams: { category: id } }); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/interceptor.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpRequest, HttpHandler } from '@angular/common/http'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class InterceptorService { 8 | 9 | constructor() { } 10 | 11 | intercept(request: HttpRequest, next: HttpHandler, ) { 12 | request = request.clone({ 13 | setHeaders: { 14 | 'Content-Type' : 'application/json', 15 | 'Authorization': `Bearer ${this.getToken()}` 16 | } 17 | }); 18 | 19 | return next.handle(request); 20 | } 21 | 22 | getToken() { 23 | return localStorage.getItem('token') 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/navbar/navbar.component.css: -------------------------------------------------------------------------------- 1 | .nav-item a:hover { 2 | cursor: pointer; 3 | } 4 | 5 | .nav-link { 6 | color: #f8f9fa!important; 7 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import { CarsService } from 'src/app/cars/cars.service'; 4 | import { Category } from 'src/app/cars/category.model'; 5 | import { NotificationsService } from '../notifications.service'; 6 | 7 | @Component({ 8 | selector: 'app-navbar', 9 | templateUrl: './navbar.component.html', 10 | styleUrls: ['./navbar.component.css'] 11 | }) 12 | export class NavbarComponent implements OnInit { 13 | token: string; 14 | constructor( 15 | private router: Router, 16 | private notificationsService: NotificationsService) { } 17 | 18 | ngOnInit(): void { 19 | this.getToken(); 20 | this.notificationsService.subscribe(); 21 | } 22 | 23 | getToken() { 24 | this.token = localStorage.getItem('token') 25 | } 26 | 27 | route(param) { 28 | console.log(param) 29 | this.router.navigate([param]) 30 | } 31 | 32 | chanheNav(event) { 33 | console.log(event) 34 | } 35 | 36 | logout() { 37 | localStorage.removeItem('token') 38 | this.getToken() 39 | this.router.navigate(['auth']) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pagination/pagination.component.css: -------------------------------------------------------------------------------- 1 | .page-link { 2 | cursor: pointer; 3 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pagination/pagination.component.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pagination/pagination.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; 2 | import { CarsService } from 'src/app/cars/cars.service'; 3 | import { Car } from 'src/app/cars/cars.model'; 4 | 5 | @Component({ 6 | selector: 'app-pagination', 7 | templateUrl: './pagination.component.html', 8 | styleUrls: ['./pagination.component.css'] 9 | }) 10 | export class PaginationComponent implements OnInit { 11 | @Input('queryString') queryString; 12 | @Output('emitter') emitter = new EventEmitter>(); 13 | constructor(private carService: CarsService) { } 14 | 15 | ngOnInit(): void { 16 | } 17 | 18 | changePage() { 19 | this.carService.search(this.queryString).subscribe(res => { 20 | this.emitter.emit(res); 21 | }) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pop-up/animations.ts: -------------------------------------------------------------------------------- 1 | import { 2 | trigger, state, style, animate, transition, 3 | } from '@angular/animations'; 4 | import { getCurrencySymbol } from '@angular/common'; 5 | 6 | export const openCloseAnimation = trigger('openClose', [ 7 | state('open', style({ 8 | opacity: 1, 9 | transform: 'scale(1)', 10 | })), 11 | state('closed', style({ 12 | opacity: 0, 13 | transform: 'scale(0)', 14 | display: 'none', 15 | })), 16 | transition('open => closed, closed => open', [ 17 | animate('0.25s') 18 | ]), 19 | ]); 20 | 21 | export const openCloseShadeAnimation = trigger('openCloseShade', [ 22 | state('open', style({ 23 | opacity: .7, 24 | backgroundColor: 'grey', 25 | })), 26 | state('closed', style({ 27 | opacity: 0, 28 | display: 'none', 29 | })), 30 | transition('open => closed, closed => open', [ 31 | animate('0.25s') 32 | ]), 33 | ]); -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pop-up/pop-up.component.css: -------------------------------------------------------------------------------- 1 | 2 | .shade { 3 | z-index: 1000; 4 | position: fixed; 5 | background-color: grey; 6 | box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2), 0px 8px 10px 1px rgba(0, 0, 0, 0.14), 0px 3px 14px 2px rgba(0, 0, 0, 0.12); 7 | opacity: 0.8; 8 | bottom: 0; 9 | left: 0; 10 | right: 0; 11 | top: 0; 12 | background: transparent; 13 | } 14 | .pop-up { 15 | z-index: 1001; 16 | display: flex; 17 | flex-direction: column; 18 | justify-content: center; 19 | box-sizing: border-box; 20 | position: fixed; 21 | left: 0; 22 | right: 0; 23 | top: 0; 24 | padding: 1ch; 25 | height: 100vh; 26 | } 27 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pop-up/pop-up.component.html: -------------------------------------------------------------------------------- 1 |
 
2 |
3 | 4 |
-------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pop-up/pop-up.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { PopUpComponent } from './pop-up.component'; 4 | 5 | describe('PopUpComponent', () => { 6 | let component: PopUpComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ PopUpComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(PopUpComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/pop-up/pop-up.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnInit } from '@angular/core'; 2 | import { openCloseAnimation, openCloseShadeAnimation } from './animations'; 3 | import { FormBuilder, FormGroup } from '@angular/forms'; 4 | 5 | @Component({ 6 | selector: 'app-pop-up', 7 | templateUrl: './pop-up.component.html', 8 | styleUrls: ['./pop-up.component.scss'], 9 | animations: [ 10 | openCloseAnimation, 11 | openCloseShadeAnimation, 12 | ] 13 | }) 14 | export class PopUpComponent implements OnInit { 15 | @Input() isOpen = false; 16 | @Input() permissionsInput; 17 | 18 | permissionsForm: FormGroup; 19 | constructor() { 20 | 21 | } 22 | 23 | ngOnInit() { 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/rouer-ext.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Router, RouterEvent, NavigationEnd } from '@angular/router'; 3 | 4 | @Injectable() 5 | export class RouterExtService { 6 | 7 | private previousUrl: string = undefined; 8 | private currentUrl: string = undefined; 9 | 10 | constructor(private router: Router) { 11 | this.currentUrl = this.router.url; 12 | router.events.subscribe(event => { 13 | if (event instanceof NavigationEnd) { 14 | this.previousUrl = this.currentUrl; 15 | this.currentUrl = event.url; 16 | } 17 | }); 18 | } 19 | 20 | public getPreviousUrl() { 21 | return this.previousUrl; 22 | } 23 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/shared-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { HomeComponent } from './home/home.component'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: '', 8 | component: HomeComponent 9 | }, 10 | ]; 11 | 12 | @NgModule({ 13 | imports: [ 14 | RouterModule.forChild(routes) 15 | ], 16 | exports: [RouterModule] 17 | }) 18 | export class SharedRoutingModule { } 19 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/statistics/statistics.model.ts: -------------------------------------------------------------------------------- 1 | export interface Statistics { 2 | totalCarAds: number; 3 | totalRentedCars: number; 4 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/app/shared/statistics/statistics.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { environment } from 'src/environments/environment'; 4 | import { Observable } from 'rxjs'; 5 | import { Statistics } from './statistics.model'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class StatisticsService { 11 | carsPath: string = environment.statisticsApiUrl + 'statistics'; 12 | 13 | constructor(private http: HttpClient) { } 14 | 15 | getStatistics(): Observable { 16 | return this.http.get(this.carsPath); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/assets/.gitkeep -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/assets/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/assets/1.jpg -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/assets/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/assets/2.jpg -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/assets/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/assets/3.jpg -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/assets/M8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/assets/M8.jpg -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/assets/RS7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/assets/RS7.jpg -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/assets/S63.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/assets/S63.jpg -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | identityApiUrl: 'http://34.122.74.94:5001/', 4 | dealersApiUrl: 'http://35.184.122.109:5002/', 5 | statisticsApiUrl: 'http://35.223.2.95:5003/', 6 | notificationsUrl: 'http://35.202.99.212:5004/' 7 | }; 8 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/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 | identityApiUrl: 'http://localhost:5001/', 8 | dealersApiUrl: 'http://localhost:5002/', 9 | statisticsApiUrl: 'http://localhost:5003/', 10 | notificationsUrl: 'http://localhost:5004/' 11 | }; 12 | 13 | /* 14 | * For easier debugging in development mode, you can import the following file 15 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 16 | * 17 | * This import should be commented out in production mode because it will have a negative impact 18 | * on performance if an error is thrown. 19 | */ 20 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 21 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Client/src/favicon.ico -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CarRentalSystem 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/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 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import '~ngx-toastr/toastr'; 3 | 4 | 5 | 6 | form { 7 | border: 1px solid gray; 8 | margin: 10%; 9 | padding: 5% 5%; 10 | } 11 | 12 | button:disabled { 13 | cursor: not-allowed; 14 | } 15 | 16 | h3 { 17 | color: #555b61!important; 18 | } 19 | 20 | label { 21 | color: #555b61!important; 22 | } 23 | 24 | .card img { 25 | width: 100%; 26 | height: 12rem; 27 | } -------------------------------------------------------------------------------- /Code/Sample Application/Client/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 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Code/Sample Application/Client/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /Code/Sample Application/README.md: -------------------------------------------------------------------------------- 1 | Make sure you start Docker Desktop and use Docker Compose V1 before running the application. 2 | 3 | To start: 4 | 1. Run `docker-compose build` 5 | 2. Run `docker-compose up -d` 6 | 7 | To stop: 8 | 1. Run `docker-compose down` 9 | 2. Run `docker volume prune` 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/.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 -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/CarRentalSystem.Admin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | $(DockerDefaultDockerfile) 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Controllers 2 | { 3 | using System.Diagnostics; 4 | using CarRentalSystem.Infrastructure; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Models; 7 | 8 | public class HomeController : Controller 9 | { 10 | public IActionResult Index() 11 | { 12 | if (this.User.IsAdministrator()) 13 | { 14 | return this.RedirectToAction(nameof(StatisticsController.Index), "Statistics"); 15 | } 16 | 17 | return View(); 18 | } 19 | 20 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 21 | public IActionResult Error() 22 | => View(new ErrorViewModel 23 | { 24 | RequestId = Activity.Current?.Id ?? this.HttpContext.TraceIdentifier 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Controllers/StatisticsController.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Controllers 2 | { 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Services.Statistics; 6 | 7 | public class StatisticsController : AdministrationController 8 | { 9 | private readonly IStatisticsService statistics; 10 | 11 | public StatisticsController(IStatisticsService statistics) 12 | => this.statistics = statistics; 13 | 14 | public async Task Index() 15 | => View(await this.statistics.Full()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 7 | WORKDIR /src 8 | COPY ["CarRentalSystem.Admin/CarRentalSystem.Admin.csproj", "CarRentalSystem.Admin/"] 9 | COPY ["CarRentalSystem/CarRentalSystem.csproj", "CarRentalSystem/"] 10 | RUN dotnet restore "CarRentalSystem.Admin/CarRentalSystem.Admin.csproj" 11 | COPY . . 12 | WORKDIR "/src/CarRentalSystem.Admin" 13 | RUN dotnet build "CarRentalSystem.Admin.csproj" -c Debug -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CarRentalSystem.Admin.csproj" -c Debug -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CarRentalSystem.Admin.dll"] -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/Dealers/DealerDetailsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models.Dealers 2 | { 3 | public class DealerDetailsOutputModel 4 | { 5 | public int Id { get; set; } 6 | 7 | public string Name { get; set; } 8 | 9 | public string PhoneNumber { get; set; } 10 | 11 | public int TotalCarAds { get; private set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/Dealers/DealerFormModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models.Dealers 2 | { 3 | using System.ComponentModel; 4 | using System.ComponentModel.DataAnnotations; 5 | using CarRentalSystem.Models; 6 | 7 | public class DealerFormModel : IMapFrom 8 | { 9 | [Required] 10 | public string Name { get; set; } 11 | 12 | [Required] 13 | [DisplayName("Phone Number")] 14 | public string PhoneNumber { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/Dealers/DealerInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models.Dealers 2 | { 3 | using CarRentalSystem.Models; 4 | 5 | public class DealerInputModel : IMapFrom 6 | { 7 | public string Name { get; set; } 8 | 9 | public string PhoneNumber { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models 2 | { 3 | public class ErrorViewModel 4 | { 5 | public string RequestId { get; set; } 6 | 7 | public bool ShowRequestId => !string.IsNullOrEmpty(this.RequestId); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/Identity/LoginFormModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models.Identity 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | public class LoginFormModel 6 | { 7 | [Required] 8 | [EmailAddress] 9 | [Display(Name = "Email Address")] 10 | public string Email { get; set; } 11 | 12 | [Required] 13 | public string Password { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/Identity/UserInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models.Identity 2 | { 3 | using CarRentalSystem.Models; 4 | 5 | public class UserInputModel : IMapFrom 6 | { 7 | public string Email { get; set; } 8 | 9 | public string Password { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/Identity/UserOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models.Identity 2 | { 3 | public class UserOutputModel 4 | { 5 | public UserOutputModel(string token) 6 | { 7 | this.Token = token; 8 | } 9 | 10 | public string Token { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Models/Statistics/StatisticsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Models.Statistics 2 | { 3 | public class StatisticsOutputModel 4 | { 5 | public int TotalCarAds { get; set; } 6 | 7 | public int TotalRentedCars { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | => CreateHostBuilder(args).Build().Run(); 10 | 11 | public static IHostBuilder CreateHostBuilder(string[] args) 12 | => Host.CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => webBuilder 14 | .UseStartup()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CarRentalSystem.Admin": { 5 | "commandName": "Project", 6 | "launchBrowser": true, 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "applicationUrl": "https://localhost:4000;http://localhost:5000" 11 | }, 12 | "Docker": { 13 | "commandName": "Docker", 14 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 15 | "publishAllPorts": true, 16 | "useSSL": true 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Services/Dealers/IDealersService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Services.Dealers 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Models.Dealers; 6 | using Refit; 7 | 8 | public interface IDealersService 9 | { 10 | [Get("/Dealers")] 11 | Task> All(); 12 | 13 | [Get("/Dealers/{id}")] 14 | Task Details(int id); 15 | 16 | [Put("/Dealers/{id}")] 17 | Task Edit(int id, DealerInputModel dealer); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Services/Identity/IIdentityService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Services.Identity 2 | { 3 | using System.Threading.Tasks; 4 | using Models.Identity; 5 | using Refit; 6 | 7 | public interface IIdentityService 8 | { 9 | [Post("/Identity/Login")] 10 | Task Login([Body] UserInputModel loginInput); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Services/ServiceEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Services 2 | { 3 | using System; 4 | using System.Linq; 5 | 6 | public class ServiceEndpoints 7 | { 8 | public string Identity { get; private set; } 9 | 10 | public string Statistics { get; private set; } 11 | 12 | public string Dealers { get; private set; } 13 | 14 | public string this[string service] 15 | => this.GetType() 16 | .GetProperties() 17 | .Where(pr => string 18 | .Equals(pr.Name, service, StringComparison.CurrentCultureIgnoreCase)) 19 | .Select(pr => (string) pr.GetValue(this)) 20 | .FirstOrDefault() 21 | ?? throw new InvalidOperationException( 22 | $"External service with name '{service}' does not exists."); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Services/Statistics/IStatisticsService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Admin.Services.Statistics 2 | { 3 | using System.Threading.Tasks; 4 | using Models.Statistics; 5 | using Refit; 6 | 7 | public interface IStatisticsService 8 | { 9 | [Get("/Statistics")] 10 | Task Full(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Views/Statistics/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model StatisticsOutputModel 2 | 3 |
4 |

@Model.TotalCarAds Car Ads | @Model.TotalRentedCars Rented Cars

5 |
-------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using CarRentalSystem.Admin 2 | @using CarRentalSystem.Admin.Infrastructure 3 | @using CarRentalSystem.Admin.Models 4 | @using CarRentalSystem.Admin.Models.Dealers 5 | @using CarRentalSystem.Admin.Models.Identity 6 | @using CarRentalSystem.Admin.Models.Statistics 7 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 8 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Code/Sample Application/Server/CarRentalSystem.Admin/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Admin/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/CarRentalSystem.Dealers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 8728cfc4-6790-4e0b-955b-7b853c02aeeb 6 | Linux 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Configurations/CategoryConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Configurations 2 | { 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Models; 6 | 7 | using static DataConstants.Categories; 8 | using static CarRentalSystem.Data.DataConstants.Common; 9 | 10 | internal class CategoryConfiguration : IEntityTypeConfiguration 11 | { 12 | public void Configure(EntityTypeBuilder builder) 13 | { 14 | builder 15 | .HasKey(c => c.Id); 16 | 17 | builder 18 | .Property(c => c.Name) 19 | .IsRequired() 20 | .HasMaxLength(MaxNameLength); 21 | 22 | builder 23 | .Property(c => c.Description) 24 | .IsRequired() 25 | .HasMaxLength(MaxDescriptionLength); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Configurations/ManufacturerConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Configurations 2 | { 3 | using Models; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | 7 | using static CarRentalSystem.Data.DataConstants.Common; 8 | 9 | internal class ManufacturerConfiguration : IEntityTypeConfiguration 10 | { 11 | public void Configure(EntityTypeBuilder builder) 12 | { 13 | builder 14 | .HasKey(m => m.Id); 15 | 16 | builder 17 | .Property(m => m.Name) 18 | .IsRequired() 19 | .HasMaxLength(MaxNameLength); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/DataConstants.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data 2 | { 3 | public class DataConstants 4 | { 5 | public class Categories 6 | { 7 | public const int MinDescriptionLength = 20; 8 | public const int MaxDescriptionLength = 1000; 9 | } 10 | 11 | public class Options 12 | { 13 | public const int MinNumberOfSeats = 2; 14 | public const int MaxNumberOfSeats = 50; 15 | } 16 | 17 | public class Dealers 18 | { 19 | public const int MinPhoneNumberLength = 5; 20 | public const int MaxPhoneNumberLength = 20; 21 | public const string PhoneNumberRegularExpression = @"\+[0-9]*"; 22 | } 23 | 24 | public class CarAds 25 | { 26 | public const int MinModelLength = 2; 27 | public const int MaxModelLength = 20; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/DealersDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data 2 | { 3 | using System.Reflection; 4 | using CarRentalSystem.Data; 5 | using Models; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | public class DealersDbContext : MessageDbContext 9 | { 10 | public DealersDbContext(DbContextOptions options) 11 | : base(options) 12 | { 13 | } 14 | 15 | public DbSet CarAds { get; set; } 16 | 17 | public DbSet Categories { get; set; } 18 | 19 | public DbSet Manufacturers { get; set; } 20 | 21 | public DbSet Dealers { get; set; } 22 | 23 | protected override Assembly ConfigurationsAssembly => Assembly.GetExecutingAssembly(); 24 | } 25 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Migrations/20200618110013_LocationColumn.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace CarRentalSystem.Dealers.Data.Migrations 4 | { 5 | public partial class LocationColumn : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.AddColumn( 10 | name: "Location", 11 | table: "CarAds", 12 | nullable: true); 13 | } 14 | 15 | protected override void Down(MigrationBuilder migrationBuilder) 16 | { 17 | migrationBuilder.DropColumn( 18 | name: "Location", 19 | table: "CarAds"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Migrations/20200710164708_MessagesTable.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace CarRentalSystem.Dealers.Data.Migrations 4 | { 5 | public partial class MessagesTable : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.CreateTable( 10 | name: "Messages", 11 | columns: table => new 12 | { 13 | Id = table.Column(nullable: false) 14 | .Annotation("SqlServer:Identity", "1, 1"), 15 | Type = table.Column(nullable: false), 16 | Published = table.Column(nullable: false), 17 | serializedData = table.Column(nullable: false) 18 | }, 19 | constraints: table => 20 | { 21 | table.PrimaryKey("PK_Messages", x => x.Id); 22 | }); 23 | } 24 | 25 | protected override void Down(MigrationBuilder migrationBuilder) 26 | { 27 | migrationBuilder.DropTable( 28 | name: "Messages"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Models/CarAd.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Models 2 | { 3 | public class CarAd 4 | { 5 | public int Id { get; set; } 6 | 7 | public string Model { get; set; } 8 | 9 | public string ImageUrl { get; set; } 10 | 11 | public decimal PricePerDay { get; set; } 12 | 13 | public Options Options { get; set; } 14 | 15 | public string Location { get; set; } 16 | 17 | public bool IsAvailable { get; set; } 18 | 19 | public int DealerId { get; set; } 20 | 21 | public Dealer Dealer { get; set; } 22 | 23 | public int ManufacturerId { get; set; } 24 | 25 | public Manufacturer Manufacturer { get; set; } 26 | 27 | public int CategoryId { get; set; } 28 | 29 | public Category Category { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Models/Category.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class Category 6 | { 7 | public int Id { get; set; } 8 | 9 | public string Name { get; set; } 10 | 11 | public string Description { get; set; } 12 | 13 | public IEnumerable CarAds { get; set; } = new List(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Models/Dealer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class Dealer 6 | { 7 | public int Id { get; set; } 8 | 9 | public string Name { get; set; } 10 | 11 | public string PhoneNumber { get; set; } 12 | 13 | public string UserId { get; set; } 14 | 15 | public ICollection CarAds { get; set; } = new List(); 16 | } 17 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Models/Manufacturer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class Manufacturer 6 | { 7 | public int Id { get; set; } 8 | 9 | public string Name { get; set; } 10 | 11 | public IEnumerable CarAds { get; set; } = new List(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Models/Options.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Models 2 | { 3 | public class Options 4 | { 5 | public bool HasClimateControl { get; set; } 6 | 7 | public int NumberOfSeats { get; set; } 8 | 9 | public TransmissionType TransmissionType { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Data/Models/TransmissionType.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Data.Models 2 | { 3 | public enum TransmissionType 4 | { 5 | Manual = 1, 6 | Automatic = 2 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 7 | WORKDIR /src 8 | COPY ["CarRentalSystem.Dealers/CarRentalSystem.Dealers.csproj", "CarRentalSystem.Dealers/"] 9 | COPY ["CarRentalSystem/CarRentalSystem.csproj", "CarRentalSystem/"] 10 | RUN dotnet restore "CarRentalSystem.Dealers/CarRentalSystem.Dealers.csproj" 11 | COPY . . 12 | WORKDIR "/src/CarRentalSystem.Dealers" 13 | RUN dotnet build "CarRentalSystem.Dealers.csproj" -c Debug -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CarRentalSystem.Dealers.csproj" -c Debug -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CarRentalSystem.Dealers.dll"] -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/CarAds/CarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.CarAds 2 | { 3 | using AutoMapper; 4 | using CarRentalSystem.Models; 5 | using Data.Models; 6 | 7 | public class CarAdOutputModel : IMapFrom 8 | { 9 | public int Id { get; set; } 10 | 11 | public string Manufacturer { get; set; } 12 | 13 | public string Model { get; set; } 14 | 15 | public string ImageUrl { get; set; } 16 | 17 | public string Category { get; set; } 18 | 19 | public decimal PricePerDay { get; set; } 20 | 21 | public virtual void Mapping(Profile mapper) 22 | => mapper 23 | .CreateMap() 24 | .ForMember(ad => ad.Manufacturer, cfg => cfg 25 | .MapFrom(ad => ad.Manufacturer.Name)) 26 | .ForMember(ad => ad.Category, cfg => cfg 27 | .MapFrom(ad => ad.Category.Name)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/CarAds/CarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.CarAds 2 | { 3 | using System.Collections.Generic; 4 | 5 | public abstract class CarAdsOutputModel 6 | { 7 | protected CarAdsOutputModel( 8 | IEnumerable carAds, 9 | int page, 10 | int totalCarAds) 11 | { 12 | this.CarAds = carAds; 13 | this.Page = page; 14 | this.TotalCarAds = totalCarAds; 15 | } 16 | 17 | public IEnumerable CarAds { get; } 18 | 19 | public int Page { get; } 20 | 21 | public int TotalCarAds { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/CarAds/CarAdsQuery.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.CarAds 2 | { 3 | public class CarAdsQuery 4 | { 5 | public string Manufacturer { get; set; } 6 | 7 | public int? Category { get; set; } 8 | 9 | public string Dealer { get; set; } 10 | 11 | public decimal? MinPricePerDay { get; set; } 12 | 13 | public decimal? MaxPricePerDay { get; set; } 14 | 15 | public string SortBy { get; set; } 16 | 17 | public string Order { get; set; } 18 | 19 | public int Page { get; set; } = 1; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/CarAds/CreateCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.CarAds 2 | { 3 | public class CreateCarAdOutputModel 4 | { 5 | public CreateCarAdOutputModel(int carAdId) 6 | => this.CarAdId = carAdId; 7 | 8 | public int CarAdId { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/CarAds/MineCarAdOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.CarAds 2 | { 3 | using AutoMapper; 4 | using Data.Models; 5 | 6 | public class MineCarAdOutputModel : CarAdOutputModel 7 | { 8 | public bool IsAvailable { get; private set; } 9 | 10 | public override void Mapping(Profile mapper) 11 | => mapper 12 | .CreateMap() 13 | .IncludeBase(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/CarAds/MineCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.CarAds 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class MineCarAdsOutputModel : CarAdsOutputModel 6 | { 7 | public MineCarAdsOutputModel( 8 | IEnumerable carAds, 9 | int page, 10 | int totalCarAds) 11 | : base(carAds, page, totalCarAds) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/CarAds/SearchCarAdsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.CarAds 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class SearchCarAdsOutputModel : CarAdsOutputModel 6 | { 7 | public SearchCarAdsOutputModel( 8 | IEnumerable carAds, 9 | int page, 10 | int totalCarAds) 11 | : base(carAds, page, totalCarAds) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/Categories/CategoryOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.Categories 2 | { 3 | using System.Linq; 4 | using AutoMapper; 5 | using CarRentalSystem.Models; 6 | using Data.Models; 7 | 8 | public class CategoryOutputModel : IMapFrom 9 | { 10 | public int Id { get; private set; } 11 | 12 | public string Name { get; private set; } = default!; 13 | 14 | public string Description { get; private set; } = default!; 15 | 16 | public int TotalCarAds { get; set; } 17 | 18 | public void Mapping(Profile profile) 19 | => profile 20 | .CreateMap() 21 | .ForMember(c => c.TotalCarAds, cfg => cfg 22 | .MapFrom(c => c.CarAds.Count())); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/Dealers/CreateDealerInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.Dealers 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | using static Data.DataConstants.Dealers; 6 | using static CarRentalSystem.Data.DataConstants.Common; 7 | 8 | public class CreateDealerInputModel 9 | { 10 | [Required] 11 | [MinLength(MinNameLength)] 12 | [MaxLength(MaxNameLength)] 13 | public string Name { get; set; } 14 | 15 | [Required] 16 | [MinLength(MinPhoneNumberLength)] 17 | [MaxLength(MaxPhoneNumberLength)] 18 | [RegularExpression(PhoneNumberRegularExpression)] 19 | public string PhoneNumber { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/Dealers/DealerDetailsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.Dealers 2 | { 3 | using System.Linq; 4 | using AutoMapper; 5 | using Data.Models; 6 | 7 | public class DealerDetailsOutputModel : DealerOutputModel 8 | { 9 | public int TotalCarAds { get; private set; } 10 | 11 | public void Mapping(Profile mapper) 12 | => mapper 13 | .CreateMap() 14 | .IncludeBase() 15 | .ForMember(d => d.TotalCarAds, cfg => cfg 16 | .MapFrom(d => d.CarAds.Count())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/Dealers/DealerOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.Dealers 2 | { 3 | using CarRentalSystem.Models; 4 | using Data.Models; 5 | 6 | public class DealerOutputModel : IMapFrom 7 | { 8 | public int Id { get; set; } 9 | 10 | public string Name { get; set; } 11 | 12 | public string PhoneNumber { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Models/Dealers/EditDealerInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Models.Dealers 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | using static Data.DataConstants.Dealers; 6 | using static CarRentalSystem.Data.DataConstants.Common; 7 | 8 | public class EditDealerInputModel 9 | { 10 | [Required] 11 | [MinLength(MinNameLength)] 12 | [MaxLength(MaxNameLength)] 13 | public string Name { get; set; } 14 | 15 | [Required] 16 | [MinLength(MinPhoneNumberLength)] 17 | [MaxLength(MaxPhoneNumberLength)] 18 | [RegularExpression(PhoneNumberRegularExpression)] 19 | public string PhoneNumber { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | => CreateHostBuilder(args).Build().Run(); 10 | 11 | public static IHostBuilder CreateHostBuilder(string[] args) 12 | => Host.CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => webBuilder 14 | .UseStartup()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CarRentalSystem.Dealers": { 5 | "commandName": "Project", 6 | "launchBrowser": false, 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "applicationUrl": "https://localhost:4002;http://localhost:5002" 11 | }, 12 | "Docker": { 13 | "commandName": "Docker", 14 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 15 | "publishAllPorts": true, 16 | "useSSL": true 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Services/CarAds/ICarAdService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Services.CarAds 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using CarRentalSystem.Services; 6 | using Data.Models; 7 | using Models.CarAds; 8 | 9 | public interface ICarAdService : IDataService 10 | { 11 | Task Find(int id); 12 | 13 | Task Delete(int id); 14 | 15 | Task> GetListings(CarAdsQuery query); 16 | 17 | Task> Mine(int dealerId, CarAdsQuery query); 18 | 19 | Task GetDetails(int id); 20 | 21 | Task Total(CarAdsQuery query); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Services/Categories/CategoryService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Services.Categories 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using AutoMapper; 6 | using CarRentalSystem.Services; 7 | using Data; 8 | using Data.Models; 9 | using Microsoft.EntityFrameworkCore; 10 | using Models.Categories; 11 | 12 | public class CategoryService : DataService, ICategoryService 13 | { 14 | private readonly IMapper mapper; 15 | 16 | public CategoryService(DealersDbContext db, IMapper mapper) 17 | : base(db) 18 | => this.mapper = mapper; 19 | 20 | public async Task Find(int categoryId) 21 | => await this.Data.FindAsync(categoryId); 22 | 23 | public async Task> GetAll() 24 | => await this.mapper 25 | .ProjectTo(this.All()) 26 | .ToListAsync(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Services/Categories/ICategoryService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Services.Categories 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Data.Models; 6 | using Models.Categories; 7 | 8 | public interface ICategoryService 9 | { 10 | Task Find(int categoryId); 11 | 12 | Task> GetAll(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Services/Dealers/IDealerService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Services.Dealers 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using CarRentalSystem.Services; 6 | using Data.Models; 7 | using Models.Dealers; 8 | 9 | public interface IDealerService : IDataService 10 | { 11 | Task FindByUser(string userId); 12 | 13 | Task FindById(int id); 14 | 15 | Task GetIdByUser(string userId); 16 | 17 | Task HasCarAd(int dealerId, int carAdId); 18 | 19 | Task IsDealer(string userId); 20 | 21 | Task> GetAll(); 22 | 23 | Task GetDetails(int id); 24 | 25 | Task GetDetailsByCarId(int carAdId); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Services/Manufacturers/IManufacturerService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Services.Manufacturers 2 | { 3 | using System.Threading.Tasks; 4 | using Data.Models; 5 | 6 | public interface IManufacturerService 7 | { 8 | Task FindByName(string name); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/Services/Manufacturers/ManufacturerService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Services.Manufacturers 2 | { 3 | using System.Threading.Tasks; 4 | using CarRentalSystem.Services; 5 | using Data; 6 | using Data.Models; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | public class ManufacturerService : DataService, IManufacturerService 10 | { 11 | public ManufacturerService(DealersDbContext db) 12 | : base(db) 13 | { 14 | } 15 | 16 | public async Task FindByName(string name) 17 | => await this 18 | .All() 19 | .FirstOrDefaultAsync(m => m.Name == name); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Dealers/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/CarRentalSystem.Identity.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | d560327c-879b-4068-a55b-0178f145e650 6 | Linux 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Data/DataConstants.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Data 2 | { 3 | public class DataConstants 4 | { 5 | public class Identity 6 | { 7 | public const int MinEmailLength = 3; 8 | public const int MaxEmailLength = 50; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Data/IdentityDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Data 2 | { 3 | using System.Reflection; 4 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore; 6 | using Models; 7 | 8 | public class IdentityDbContext : IdentityDbContext 9 | { 10 | public IdentityDbContext(DbContextOptions options) 11 | : base(options) 12 | { 13 | } 14 | 15 | protected override void OnModelCreating(ModelBuilder builder) 16 | { 17 | builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); 18 | 19 | base.OnModelCreating(builder); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Data/Models/User.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Data.Models 2 | { 3 | using Microsoft.AspNetCore.Identity; 4 | 5 | public class User : IdentityUser 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 7 | WORKDIR /src 8 | COPY ["CarRentalSystem.Identity/CarRentalSystem.Identity.csproj", "CarRentalSystem.Identity/"] 9 | COPY ["CarRentalSystem/CarRentalSystem.csproj", "CarRentalSystem/"] 10 | RUN dotnet restore "CarRentalSystem.Identity/CarRentalSystem.Identity.csproj" 11 | COPY . . 12 | WORKDIR "/src/CarRentalSystem.Identity" 13 | RUN dotnet build "CarRentalSystem.Identity.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CarRentalSystem.Identity.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CarRentalSystem.Identity.dll"] -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/IdentitySettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity 2 | { 3 | public class IdentitySettings 4 | { 5 | public string AdminPassword { get; private set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Infrastructure/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Infrastructure 2 | { 3 | using Data; 4 | using Data.Models; 5 | using Microsoft.AspNetCore.Identity; 6 | using Microsoft.Extensions.DependencyInjection; 7 | 8 | public static class ServiceCollectionExtensions 9 | { 10 | public static IServiceCollection AddUserStorage( 11 | this IServiceCollection services) 12 | { 13 | services 14 | .AddIdentity(options => 15 | { 16 | options.Password.RequiredLength = 6; 17 | options.Password.RequireDigit = false; 18 | options.Password.RequireLowercase = false; 19 | options.Password.RequireNonAlphanumeric = false; 20 | options.Password.RequireUppercase = false; 21 | }) 22 | .AddEntityFrameworkStores(); 23 | 24 | return services; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Models/Identity/ChangePasswordInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Models.Identity 2 | { 3 | public class ChangePasswordInputModel 4 | { 5 | public string CurrentPassword { get; set; } 6 | 7 | public string NewPassword { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Models/Identity/UserInputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Models.Identity 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | using static Data.DataConstants.Identity; 6 | 7 | public class UserInputModel 8 | { 9 | [EmailAddress] 10 | [Required] 11 | [MinLength(MinEmailLength)] 12 | [MaxLength(MaxEmailLength)] 13 | public string Email { get; set; } 14 | 15 | [Required] 16 | public string Password { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Models/Identity/UserOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Models.Identity 2 | { 3 | public class UserOutputModel 4 | { 5 | public UserOutputModel(string token) 6 | { 7 | this.Token = token; 8 | } 9 | 10 | public string Token { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | => CreateHostBuilder(args).Build().Run(); 10 | 11 | public static IHostBuilder CreateHostBuilder(string[] args) 12 | => Host.CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => webBuilder 14 | .UseStartup()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CarRentalSystem.Identity": { 5 | "commandName": "Project", 6 | "launchBrowser": false, 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "applicationUrl": "https://localhost:4001;http://localhost:5001" 11 | }, 12 | "Docker": { 13 | "commandName": "Docker", 14 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 15 | "publishAllPorts": true, 16 | "useSSL": true 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Services/Identity/IIdentityService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Services.Identity 2 | { 3 | using System.Threading.Tasks; 4 | using CarRentalSystem.Services; 5 | using Data.Models; 6 | using Models.Identity; 7 | 8 | public interface IIdentityService 9 | { 10 | Task> Register(UserInputModel userInput); 11 | 12 | Task> Login(UserInputModel userInput); 13 | 14 | Task ChangePassword(string userId, ChangePasswordInputModel changePasswordInput); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/Services/Identity/ITokenGeneratorService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Identity.Services.Identity 2 | { 3 | using System.Collections.Generic; 4 | using Data.Models; 5 | 6 | public interface ITokenGeneratorService 7 | { 8 | string GenerateToken(User user, IEnumerable roles = null); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Identity/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/CarRentalSystem.Notifications.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Notifications 2 | { 3 | public class Constants 4 | { 5 | public const string AuthenticatedUsersGroup = "AuthenticatedUsers"; 6 | 7 | public const string ReceiveNotificationEndpoint = "ReceiveNotification"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 7 | WORKDIR /src 8 | COPY ["CarRentalSystem.Notifications/CarRentalSystem.Notifications.csproj", "CarRentalSystem.Notifications/"] 9 | COPY ["CarRentalSystem/CarRentalSystem.csproj", "CarRentalSystem/"] 10 | RUN dotnet restore "CarRentalSystem.Notifications/CarRentalSystem.Notifications.csproj" 11 | COPY . . 12 | WORKDIR "/src/CarRentalSystem.Notifications" 13 | RUN dotnet build "CarRentalSystem.Notifications.csproj" -c Debug -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CarRentalSystem.Notifications.csproj" -c Debug -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CarRentalSystem.Notifications.dll"] -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/Hub/NotificationsHub.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Notifications.Hub 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.SignalR; 6 | 7 | using static Constants; 8 | 9 | public class NotificationsHub : Hub 10 | { 11 | public override async Task OnConnectedAsync() 12 | { 13 | if (this.Context.User.Identity.IsAuthenticated) 14 | { 15 | await this.Groups.AddToGroupAsync( 16 | this.Context.ConnectionId, 17 | AuthenticatedUsersGroup); 18 | } 19 | } 20 | 21 | public override async Task OnDisconnectedAsync(Exception exception) 22 | { 23 | if (this.Context.User.Identity.IsAuthenticated) 24 | { 25 | await this.Groups.RemoveFromGroupAsync( 26 | this.Context.ConnectionId, 27 | AuthenticatedUsersGroup); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/Infrastructure/JwtConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Notifications.Infrastructure 2 | { 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Authentication.JwtBearer; 5 | 6 | public class JwtConfiguration 7 | { 8 | public static JwtBearerEvents BearerEvents 9 | => new JwtBearerEvents 10 | { 11 | OnMessageReceived = context => 12 | { 13 | var accessToken = context.Request.Query["access_token"]; 14 | 15 | var path = context.HttpContext.Request.Path; 16 | if (!string.IsNullOrEmpty(accessToken) && 17 | path.StartsWithSegments("/notifications")) 18 | { 19 | context.Token = accessToken; 20 | } 21 | 22 | return Task.CompletedTask; 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/Messages/CarAdCreatedConsumer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Notifications.Messages 2 | { 3 | using System.Threading.Tasks; 4 | using CarRentalSystem.Messages.Dealers; 5 | using Hub; 6 | using MassTransit; 7 | using Microsoft.AspNetCore.SignalR; 8 | 9 | using static Constants; 10 | 11 | public class CarAdCreatedConsumer : IConsumer 12 | { 13 | private readonly IHubContext hub; 14 | 15 | public CarAdCreatedConsumer(IHubContext hub) 16 | => this.hub = hub; 17 | 18 | public async Task Consume(ConsumeContext context) 19 | => await this.hub 20 | .Clients 21 | .Groups(AuthenticatedUsersGroup) 22 | .SendAsync(ReceiveNotificationEndpoint, context.Message); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/NotificationSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Notifications 2 | { 3 | public class NotificationSettings 4 | { 5 | public string AllowedOrigins { get; private set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Notifications 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | => CreateHostBuilder(args).Build().Run(); 10 | 11 | public static IHostBuilder CreateHostBuilder(string[] args) 12 | => Host.CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => webBuilder 14 | .UseStartup()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CarRentalSystem.Notifications": { 5 | "commandName": "Project", 6 | "launchBrowser": false, 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "applicationUrl": "https://localhost:4004;http://localhost:5004" 11 | }, 12 | "Docker": { 13 | "commandName": "Docker", 14 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 15 | "publishAllPorts": true, 16 | "useSSL": true 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Notifications/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/CarRentalSystem.Schedule.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Configurations/DriverConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Configurations 2 | { 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Models; 6 | 7 | public class DriverConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(c => c.Id); 13 | 14 | builder 15 | .Property(c => c.License) 16 | .IsRequired(); 17 | 18 | builder 19 | .Property(c => c.UserId) 20 | .IsRequired(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Configurations/FeedbackConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Configurations 2 | { 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Models; 6 | 7 | public class FeedbackConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(c => c.Id); 13 | 14 | builder 15 | .Property(c => c.Content) 16 | .IsRequired(); 17 | 18 | builder 19 | .HasOne(f => f.Reservation) 20 | .WithOne() 21 | .HasForeignKey(f => f.ReservationId); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Configurations/RentedCarConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Configurations 2 | { 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Models; 6 | 7 | public class RentedCarConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(c => c.Id); 13 | 14 | builder 15 | .Property(c => c.Information) 16 | .IsRequired(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Configurations/ReservationConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Configurations 2 | { 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Models; 6 | 7 | public class ReservationConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(c => c.Id); 13 | 14 | builder 15 | .HasOne(r => r.Driver) 16 | .WithMany(d => d.Reservations) 17 | .HasForeignKey(r => r.DriverId); 18 | 19 | builder 20 | .HasOne(r => r.RentedCar) 21 | .WithMany(rc => rc.Reservations) 22 | .HasForeignKey(r => r.RentedCarId); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Models/Driver.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class Driver 6 | { 7 | public int Id { get; set; } 8 | 9 | public string License { get; set; } 10 | 11 | public int YearsOfExperience { get; set; } 12 | 13 | public string UserId { get; set; } 14 | 15 | public ICollection Reservations { get; set; } = new List(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Models/Feedback.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Models 2 | { 3 | public class Feedback 4 | { 5 | public int Id { get; set; } 6 | 7 | public string Content { get; set; } 8 | 9 | public int ReservationId { get; set; } 10 | 11 | public Reservation Reservation { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Models/RentedCar.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Models 2 | { 3 | using System.Collections.Generic; 4 | 5 | public class RentedCar 6 | { 7 | public int Id { get; set; } 8 | 9 | public string Information { get; set; } 10 | 11 | public int Kilometers { get; set; } 12 | 13 | public bool HasInsurance { get; set; } 14 | 15 | public int CarAdId { get; set; } 16 | 17 | public ICollection Reservations { get; set; } = new List(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/Models/Reservation.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data.Models 2 | { 3 | using System; 4 | 5 | public class Reservation 6 | { 7 | public int Id { get; set; } 8 | 9 | public DateTime StartDate { get; set; } 10 | 11 | public DateTime EndDate { get; set; } 12 | 13 | public int DriverId { get; set; } 14 | 15 | public Driver Driver { get; set; } 16 | 17 | public int RentedCarId { get; set; } 18 | 19 | public RentedCar RentedCar { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Data/ScheduleDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Data 2 | { 3 | using System.Reflection; 4 | using Microsoft.EntityFrameworkCore; 5 | using Models; 6 | 7 | public class ScheduleDbContext : DbContext 8 | { 9 | public ScheduleDbContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | } 13 | 14 | public DbSet Drivers { get; set; } 15 | 16 | public DbSet Feedback { get; set; } 17 | 18 | public DbSet RentedCars { get; set; } 19 | 20 | public DbSet Reservations { get; set; } 21 | 22 | protected override void OnModelCreating(ModelBuilder builder) 23 | { 24 | builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); 25 | 26 | base.OnModelCreating(builder); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Messages/CarAdUpdatedConsumer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Messages 2 | { 3 | using System.Threading.Tasks; 4 | using CarRentalSystem.Messages.Dealers; 5 | using MassTransit; 6 | using Services; 7 | 8 | public class CarAdUpdatedConsumer : IConsumer 9 | { 10 | private readonly IRentedCarService rentedCars; 11 | 12 | public CarAdUpdatedConsumer(IRentedCarService rentedCars) 13 | => this.rentedCars = rentedCars; 14 | 15 | public async Task Consume(ConsumeContext context) 16 | => await this.rentedCars.UpdateInformation( 17 | context.Message.CarAdId, 18 | context.Message.Manufacturer, 19 | context.Message.Model); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | => CreateHostBuilder(args).Build().Run(); 10 | 11 | public static IHostBuilder CreateHostBuilder(string[] args) 12 | => Host.CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => webBuilder 14 | .UseStartup()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CarRentalSystem.Schedule": { 5 | "commandName": "Project", 6 | "launchBrowser": false, 7 | "applicationUrl": "https://localhost:5007;http://localhost:5006", 8 | "environmentVariables": { 9 | "ASPNETCORE_ENVIRONMENT": "Development" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Services/IRentedCarService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Services 2 | { 3 | using System.Threading.Tasks; 4 | 5 | public interface IRentedCarService 6 | { 7 | Task UpdateInformation(int carAdId, string manufacturer, string model); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Services/RentedCarService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule.Services 2 | { 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using CarRentalSystem.Services; 6 | using Data.Models; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | public class RentedCarService : DataService, IRentedCarService 10 | { 11 | public RentedCarService(DbContext db) 12 | : base(db) 13 | { 14 | } 15 | 16 | public async Task UpdateInformation(int carAdId, string manufacturer, string model) 17 | { 18 | var rentedCars = await this 19 | .All() 20 | .Where(rc => rc.CarAdId == carAdId) 21 | .ToListAsync(); 22 | 23 | foreach (var rentedCar in rentedCars) 24 | { 25 | rentedCar.Information = $"{manufacturer} {model}"; 26 | } 27 | 28 | await this.Data.SaveChangesAsync(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Schedule 2 | { 3 | using Data; 4 | using Infrastructure; 5 | using Messages; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Services; 11 | 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | => this.Configuration = configuration; 16 | 17 | public IConfiguration Configuration { get; } 18 | 19 | public void ConfigureServices(IServiceCollection services) 20 | => services 21 | .AddWebService(this.Configuration) 22 | .AddTransient() 23 | .AddMessaging( 24 | this.Configuration, 25 | typeof(CarAdUpdatedConsumer)); 26 | 27 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 28 | => app 29 | .UseWebService(env) 30 | .Initialize(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Schedule/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationSettings": { 3 | "Secret": "S0M3 M4G1C UN1C0RNS G3N3R4T3D TH1S S3CR3T" 4 | }, 5 | "ConnectionStrings": { 6 | "DefaultConnection": "Server=.;Database=CarRentalScheduleDatabase;Trusted_Connection=True;MultipleActiveResultSets=true" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft": "Warning", 12 | "Microsoft.Hosting.Lifetime": "Information" 13 | } 14 | }, 15 | "AllowedHosts": "*" 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/CarRentalSystem.Statistics.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | ..\docker-compose.dcproj 6 | 16f7d74c-c1cc-48c5-8d59-a4f46bd97ebc 7 | Linux 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Controllers/CarAdViewsController.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Controllers 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Dealers.Controllers; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Models.CarAdViews; 9 | using Services.CarAdViews; 10 | 11 | public class CarAdViewsController : ApiController 12 | { 13 | private readonly ICarAdViewService carAdViews; 14 | 15 | public CarAdViewsController(ICarAdViewService carAdViews) 16 | => this.carAdViews = carAdViews; 17 | 18 | [HttpGet] 19 | [Route(Id)] 20 | public async Task TotalViews(int id) 21 | => await this.carAdViews.GetTotalViews(id); 22 | 23 | [HttpGet] 24 | [Authorize] 25 | public async Task> TotalViews( 26 | [FromQuery] IEnumerable ids) 27 | => await this.carAdViews.GetTotalViews(ids); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Controllers/StatisticsController.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Controllers 2 | { 3 | using System.Threading.Tasks; 4 | using Dealers.Controllers; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Models.Statistics; 7 | using Services.Statistics; 8 | 9 | public class StatisticsController : ApiController 10 | { 11 | private readonly IStatisticsService statistics; 12 | 13 | public StatisticsController(IStatisticsService statistics) 14 | => this.statistics = statistics; 15 | 16 | [HttpGet] 17 | public async Task Full() 18 | => await this.statistics.Full(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Data/Configurations/CarAdViewConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Data.Configurations 2 | { 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Models; 6 | 7 | public class CarAdViewConfiguration : IEntityTypeConfiguration 8 | { 9 | public void Configure(EntityTypeBuilder builder) 10 | { 11 | builder 12 | .HasKey(v => v.Id); 13 | 14 | builder 15 | .HasIndex(v => v.CarAdId); 16 | 17 | builder 18 | .Property(v => v.UserId) 19 | .IsRequired(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Data/Migrations/20200618130926_CarAdViewIndex.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace CarRentalSystem.Statistics.Data.Migrations 4 | { 5 | public partial class CarAdViewIndex : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.CreateIndex( 10 | name: "IX_CarAdViews_CarAdId", 11 | table: "CarAdViews", 12 | column: "CarAdId"); 13 | } 14 | 15 | protected override void Down(MigrationBuilder migrationBuilder) 16 | { 17 | migrationBuilder.DropIndex( 18 | name: "IX_CarAdViews_CarAdId", 19 | table: "CarAdViews"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Data/Models/CarAdView.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Data.Models 2 | { 3 | public class CarAdView 4 | { 5 | public int Id { get; set; } 6 | 7 | public int CarAdId { get; set; } 8 | 9 | public string UserId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Data/Models/Statistics.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Data.Models 2 | { 3 | public class Statistics 4 | { 5 | public int Id { get; set; } 6 | 7 | public int TotalCarAds { get; set; } 8 | 9 | public int TotalRentedCars { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Data/StatisticsDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Data 2 | { 3 | using System.Reflection; 4 | using CarRentalSystem.Data; 5 | using Microsoft.EntityFrameworkCore; 6 | using Models; 7 | 8 | public class StatisticsDbContext : MessageDbContext 9 | { 10 | public StatisticsDbContext(DbContextOptions options) 11 | : base(options) 12 | { 13 | } 14 | 15 | public DbSet CarAdViews { get; set; } 16 | 17 | public DbSet Statistics { get; set; } 18 | 19 | protected override Assembly ConfigurationsAssembly => Assembly.GetExecutingAssembly(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 7 | WORKDIR /src 8 | COPY ["CarRentalSystem.Statistics/CarRentalSystem.Statistics.csproj", "CarRentalSystem.Statistics/"] 9 | COPY ["CarRentalSystem/CarRentalSystem.csproj", "CarRentalSystem/"] 10 | RUN dotnet restore "CarRentalSystem.Statistics/CarRentalSystem.Statistics.csproj" 11 | COPY . . 12 | WORKDIR "/src/CarRentalSystem.Statistics" 13 | RUN dotnet build "CarRentalSystem.Statistics.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CarRentalSystem.Statistics.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CarRentalSystem.Statistics.dll"] 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Messages/CarAdCreatedConsumer.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Messages 2 | { 3 | using System.Threading.Tasks; 4 | using CarRentalSystem.Messages.Dealers; 5 | using MassTransit; 6 | using Services.Statistics; 7 | 8 | public class CarAdCreatedConsumer : IConsumer 9 | { 10 | private readonly IStatisticsService statistics; 11 | 12 | public CarAdCreatedConsumer(IStatisticsService statistics) 13 | => this.statistics = statistics; 14 | 15 | public async Task Consume(ConsumeContext context) 16 | => await this.statistics.AddCarAd(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Models/CarAdViews/CarAdViewOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Models.CarAdViews 2 | { 3 | public class CarAdViewOutputModel 4 | { 5 | public int CarAdId { get; set; } 6 | 7 | public int TotalViews { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Models/Statistics/StatisticsOutputModel.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Models.Statistics 2 | { 3 | using CarRentalSystem.Models; 4 | using Data.Models; 5 | 6 | public class StatisticsOutputModel : IMapFrom 7 | { 8 | public int TotalCarAds { get; set; } 9 | 10 | public int TotalRentedCars { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Program.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics 2 | { 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | => CreateHostBuilder(args).Build().Run(); 10 | 11 | public static IHostBuilder CreateHostBuilder(string[] args) 12 | => Host.CreateDefaultBuilder(args) 13 | .ConfigureWebHostDefaults(webBuilder => webBuilder 14 | .UseStartup()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CarRentalSystem.Statistics": { 5 | "commandName": "Project", 6 | "launchBrowser": false, 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "applicationUrl": "https://localhost:4003;http://localhost:5003" 11 | }, 12 | "Docker": { 13 | "commandName": "Docker", 14 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 15 | "publishAllPorts": true, 16 | "useSSL": true 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Services/CarAdViews/ICarAdViewService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Services.CarAdViews 2 | { 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Models.CarAdViews; 6 | 7 | public interface ICarAdViewService 8 | { 9 | Task GetTotalViews(int carAdId); 10 | 11 | Task> GetTotalViews(IEnumerable ids); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Services/Statistics/IStatisticsService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Services.Statistics 2 | { 3 | using System.Threading.Tasks; 4 | using Models.Statistics; 5 | 6 | public interface IStatisticsService 7 | { 8 | Task Full(); 9 | 10 | Task AddCarAd(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/Services/Statistics/StatisticsService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Statistics.Services.Statistics 2 | { 3 | using System.Threading.Tasks; 4 | using AutoMapper; 5 | using CarRentalSystem.Services; 6 | using Data; 7 | using Data.Models; 8 | using Microsoft.EntityFrameworkCore; 9 | using Models.Statistics; 10 | 11 | public class StatisticsService : DataService, IStatisticsService 12 | { 13 | private readonly IMapper mapper; 14 | 15 | public StatisticsService(StatisticsDbContext db, IMapper mapper) 16 | : base(db) 17 | { 18 | this.mapper = mapper; 19 | } 20 | 21 | public async Task Full() 22 | => await this.mapper 23 | .ProjectTo(this.All()) 24 | .SingleOrDefaultAsync(); 25 | 26 | public async Task AddCarAd() 27 | { 28 | var statistics = await this.All().SingleOrDefaultAsync(); 29 | 30 | statistics.TotalCarAds++; 31 | 32 | await this.Data.SaveChangesAsync(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Statistics/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Watchdog/CarRentalSystem.Watchdog.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | $(DockerDefaultDockerfile) 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Watchdog/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base 2 | WORKDIR /app 3 | EXPOSE 80 4 | EXPOSE 443 5 | 6 | FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build 7 | WORKDIR /src 8 | COPY ["CarRentalSystem.Watchdog/CarRentalSystem.Watchdog.csproj", "CarRentalSystem.Watchdog/"] 9 | COPY ["CarRentalSystem/CarRentalSystem.csproj", "CarRentalSystem/"] 10 | RUN dotnet restore "CarRentalSystem.Watchdog/CarRentalSystem.Watchdog.csproj" 11 | COPY . . 12 | WORKDIR "/src/CarRentalSystem.Watchdog" 13 | RUN dotnet build "CarRentalSystem.Watchdog.csproj" -c Release -o /app/build 14 | 15 | FROM build AS publish 16 | RUN dotnet publish "CarRentalSystem.Watchdog.csproj" -c Release -o /app/publish 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "CarRentalSystem.Watchdog.dll"] 22 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Watchdog/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace CarRentalSystem.Watchdog 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 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Watchdog/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CarRentalSystem.Watchdog": { 5 | "commandName": "Project", 6 | "launchBrowser": true, 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "applicationUrl": "https://localhost:4500;http://localhost:5500" 11 | }, 12 | "Docker": { 13 | "commandName": "Docker", 14 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 15 | "publishAllPorts": true, 16 | "useSSL": true 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Watchdog/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Watchdog 2 | { 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | public class Startup 8 | { 9 | public void ConfigureServices(IServiceCollection services) 10 | { 11 | services 12 | .AddHealthChecksUI() 13 | .AddInMemoryStorage(); 14 | } 15 | 16 | public void Configure(IApplicationBuilder app) 17 | { 18 | app 19 | .UseRouting() 20 | .UseEndpoints(endpoints => endpoints 21 | .MapHealthChecksUI(healthChecks => healthChecks 22 | .UIPath = "/healthchecks")); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Watchdog/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem.Watchdog/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/ApplicationSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem 2 | { 3 | public class ApplicationSettings 4 | { 5 | public string Secret { get; private set; } 6 | 7 | public bool SeedInitialData { get; private set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Common.env: -------------------------------------------------------------------------------- 1 | # Common application settings 2 | ApplicationSettings__Secret=S0M3 M4G1C UN1C0RNS G3N3R4T3D TH1S S3CR3T 3 | ApplicationSettings__SeedInitialData=True -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem 2 | { 3 | public class Constants 4 | { 5 | public const string AdministratorRoleName = "Administrator"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Controllers/ApiController.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Dealers.Controllers 2 | { 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | [ApiController] 6 | [Route("[controller]")] 7 | public abstract class ApiController : ControllerBase 8 | { 9 | public const string PathSeparator = "/"; 10 | public const string Id = "{id}"; 11 | } 12 | } -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Data/Configuration/MessageConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Data.Configuration 2 | { 3 | using System; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 6 | using Models; 7 | 8 | public class MessageConfiguration : IEntityTypeConfiguration 9 | { 10 | public void Configure(EntityTypeBuilder builder) 11 | { 12 | builder 13 | .HasKey(m => m.Id); 14 | 15 | builder 16 | .Property("serializedData") 17 | .IsRequired() 18 | .HasField("serializedData"); 19 | 20 | builder 21 | .Property(m => m.Type) 22 | .IsRequired() 23 | .HasConversion( 24 | t => t.AssemblyQualifiedName, 25 | t => Type.GetType(t)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Data/DataConstants.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Data 2 | { 3 | public class DataConstants 4 | { 5 | public class Common 6 | { 7 | public const int MinNameLength = 2; 8 | public const int MaxNameLength = 20; 9 | public const int MaxUrlLength = 2048; 10 | public const int Zero = 0; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Data/DataSeederConstants.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Data 2 | { 3 | public class DataSeederConstants 4 | { 5 | public const string DefaultUserId = "6be0140d-91db-46e3-adc0-bba8839239c6"; 6 | public const string DefaultUserPassword = "coolcars12!"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Data/MessageDbContext.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Data 2 | { 3 | using System.Reflection; 4 | using Configuration; 5 | using Microsoft.EntityFrameworkCore; 6 | using Models; 7 | 8 | public abstract class MessageDbContext : DbContext 9 | { 10 | protected MessageDbContext(DbContextOptions options) 11 | : base(options) 12 | { 13 | } 14 | 15 | public DbSet Messages { get; set; } 16 | 17 | protected abstract Assembly ConfigurationsAssembly { get; } 18 | 19 | protected override void OnModelCreating(ModelBuilder builder) 20 | { 21 | builder.ApplyConfiguration(new MessageConfiguration()); 22 | 23 | builder.ApplyConfigurationsFromAssembly(this.ConfigurationsAssembly); 24 | 25 | base.OnModelCreating(builder); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Infrastructure/AuthorizeAdministratorAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure 2 | { 3 | using Microsoft.AspNetCore.Authorization; 4 | using static Constants; 5 | 6 | public class AuthorizeAdministratorAttribute : AuthorizeAttribute 7 | { 8 | public AuthorizeAdministratorAttribute() => this.Roles = AdministratorRoleName; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Infrastructure/ClaimsPrincipalExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure 2 | { 3 | using System.Security.Claims; 4 | 5 | using static Constants; 6 | 7 | public static class ClaimsPrincipalExtensions 8 | { 9 | public static bool IsAdministrator(this ClaimsPrincipal user) 10 | => user.IsInRole(AdministratorRoleName); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Infrastructure/ConfigurationExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure 2 | { 3 | using Microsoft.Extensions.Configuration; 4 | 5 | public static class ConfigurationExtensions 6 | { 7 | public static string GetDefaultConnectionString(this IConfiguration configuration) 8 | => configuration.GetConnectionString("DefaultConnection"); 9 | 10 | public static string GetCronJobsConnectionString(this IConfiguration configuration) 11 | => configuration.GetConnectionString("CronJobsConnection"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Infrastructure/EndpointRouteBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure 2 | { 3 | using HealthChecks.UI.Client; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; 6 | using Microsoft.AspNetCore.Routing; 7 | 8 | public static class EndpointRouteBuilderExtensions 9 | { 10 | public static IEndpointRouteBuilder MapHealthChecks(this IEndpointRouteBuilder endpoints) 11 | { 12 | endpoints.MapHealthChecks("/health", new HealthCheckOptions 13 | { 14 | ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse 15 | }); 16 | 17 | return endpoints; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Infrastructure/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public static class EnumerableExtensions 7 | { 8 | public static void ForEach(this IEnumerable enumerable, Action action) 9 | { 10 | foreach (var item in enumerable) 11 | { 12 | action(item); 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Infrastructure/InfrastructureConstants.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Infrastructure 2 | { 3 | public class InfrastructureConstants 4 | { 5 | public const string AuthenticationCookieName = "Authentication"; 6 | public const string AuthorizationHeaderName = "Authorization"; 7 | public const string AuthorizationHeaderValuePrefix = "Bearer"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/MessageQueueSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem 2 | { 3 | public class MessageQueueSettings 4 | { 5 | public MessageQueueSettings(string host, string userName, string password) 6 | { 7 | this.Host = host; 8 | this.UserName = userName; 9 | this.Password = password; 10 | } 11 | 12 | public string Host { get; } 13 | 14 | public string UserName { get; } 15 | 16 | public string Password { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Messages/Dealers/CarAdCreatedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Messages.Dealers 2 | { 3 | public class CarAdCreatedMessage 4 | { 5 | public int CarAdId { get; set; } 6 | 7 | public string Manufacturer { get; set; } 8 | 9 | public string Model { get; set; } 10 | 11 | public decimal PricePerDay { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Messages/Dealers/CarAdUpdatedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Messages.Dealers 2 | { 3 | public class CarAdUpdatedMessage 4 | { 5 | public int CarAdId { get; set; } 6 | 7 | public string Manufacturer { get; set; } 8 | 9 | public string Model { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Models/IMapFrom.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Models 2 | { 3 | using AutoMapper; 4 | 5 | public interface IMapFrom 6 | { 7 | void Mapping(Profile mapper) => mapper.CreateMap(typeof(T), this.GetType()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Services/DataService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Services 2 | { 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Data.Models; 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | public abstract class DataService : IDataService 9 | where TEntity : class 10 | { 11 | protected DataService(DbContext db) => this.Data = db; 12 | 13 | protected DbContext Data { get; } 14 | 15 | protected IQueryable All() => this.Data.Set(); 16 | 17 | public async Task MarkMessageAsPublished(int id) 18 | { 19 | var message = await this.Data.FindAsync(id); 20 | 21 | message.MarkAsPublished(); 22 | 23 | await this.Data.SaveChangesAsync(); 24 | } 25 | 26 | public async Task Save(TEntity entity, params Message[] messages) 27 | { 28 | foreach (var message in messages) 29 | { 30 | this.Data.Add(message); 31 | } 32 | 33 | this.Data.Update(entity); 34 | 35 | await this.Data.SaveChangesAsync(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Services/IDataSeeder.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Services 2 | { 3 | public interface IDataSeeder 4 | { 5 | void SeedData(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Services/IDataService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Services 2 | { 3 | using System.Threading.Tasks; 4 | using Data.Models; 5 | 6 | public interface IDataService 7 | where TEntity : class 8 | { 9 | Task MarkMessageAsPublished(int id); 10 | 11 | Task Save(TEntity entity, params Message[] messages); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Services/Identity/CurrentTokenService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Services.Identity 2 | { 3 | public class CurrentTokenService : ICurrentTokenService 4 | { 5 | private string currentToken; 6 | 7 | public string Get() => this.currentToken; 8 | 9 | public void Set(string token) => this.currentToken = token; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Services/Identity/CurrentUserService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Services.Identity 2 | { 3 | using System; 4 | using System.Security.Claims; 5 | using Infrastructure; 6 | using Microsoft.AspNetCore.Http; 7 | 8 | public class CurrentUserService : ICurrentUserService 9 | { 10 | private readonly ClaimsPrincipal user; 11 | 12 | public CurrentUserService(IHttpContextAccessor httpContextAccessor) 13 | { 14 | this.user = httpContextAccessor.HttpContext?.User; 15 | 16 | if (user == null) 17 | { 18 | throw new InvalidOperationException("This request does not have an authenticated user."); 19 | } 20 | 21 | this.UserId = this.user.FindFirstValue(ClaimTypes.NameIdentifier); 22 | } 23 | 24 | public string UserId { get; } 25 | 26 | public bool IsAdministrator => this.user.IsAdministrator(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Services/Identity/ICurrentTokenService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Services.Identity 2 | { 3 | public interface ICurrentTokenService 4 | { 5 | string Get(); 6 | 7 | void Set(string token); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/Sample Application/Server/CarRentalSystem/Services/Identity/ICurrentUserService.cs: -------------------------------------------------------------------------------- 1 | namespace CarRentalSystem.Services.Identity 2 | { 3 | public interface ICurrentUserService 4 | { 5 | string UserId { get; } 6 | 7 | bool IsAdministrator { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCClient/Program.cs: -------------------------------------------------------------------------------- 1 | namespace gRPCClient 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | using Grpc.Net.Client; 6 | using gRPCServer; 7 | 8 | public class Program 9 | { 10 | public static async Task Main() 11 | { 12 | var channel = GrpcChannel.ForAddress("https://localhost:5001"); 13 | var client = new Weather.WeatherClient(channel); 14 | 15 | var weather = new WeatherForecast { Summary = "Hot" }; 16 | var reply = await client.SaveForecastAsync(weather); 17 | 18 | Console.WriteLine(reply.Success); 19 | 20 | var result = await client.GetForecastAsync(new Empty()); 21 | 22 | Console.WriteLine(result.Summary); 23 | 24 | Console.ReadKey(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCClient/gRPCClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | all 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Hosting; 8 | 9 | namespace gRPCServer 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | CreateHostBuilder(args).Build().Run(); 16 | } 17 | 18 | // Additional configuration is required to successfully run gRPC on macOS. 19 | // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 20 | public static IHostBuilder CreateHostBuilder(string[] args) => 21 | Host.CreateDefaultBuilder(args) 22 | .ConfigureWebHostDefaults(webBuilder => 23 | { 24 | webBuilder.UseStartup(); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "gRPCServer": { 4 | "commandName": "Project", 5 | "launchBrowser": false, 6 | "applicationUrl": "https://localhost:5001", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCServer/Protos/weather.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option csharp_namespace = "gRPCServer"; 4 | 5 | package weather; 6 | 7 | service Weather { 8 | rpc SaveForecast (WeatherForecast) returns (StatusResponse); 9 | rpc GetForecast (Empty) returns (WeatherForecast); 10 | } 11 | 12 | message Empty {} 13 | 14 | message WeatherForecast { 15 | string Summary = 1; 16 | } 17 | 18 | message StatusResponse { 19 | bool success = 1; 20 | } 21 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCServer/Services/WeatherService.cs: -------------------------------------------------------------------------------- 1 | namespace gRPCServer.Services 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Grpc.Core; 8 | 9 | public class WeatherService : Weather.WeatherBase 10 | { 11 | private static readonly List Data = new List 12 | { 13 | new WeatherForecast { Summary = "Cold" } 14 | }; 15 | 16 | public override Task SaveForecast(WeatherForecast request, ServerCallContext context) 17 | { 18 | if (string.IsNullOrEmpty(request.Summary)) 19 | { 20 | return Task.FromResult(new StatusResponse { Success = false }); 21 | } 22 | 23 | Data.Add(request); 24 | 25 | return Task.FromResult(new StatusResponse { Success = true }); 26 | } 27 | 28 | public override Task GetForecast(Empty request, ServerCallContext context) 29 | => Task.FromResult(Data.OrderBy(x => Guid.NewGuid()).FirstOrDefault()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Grpc": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*", 10 | "Kestrel": { 11 | "EndpointDefaults": { 12 | "Protocols": "Http2" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Code/gRPC/gRPCServer/gRPCServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Presentation.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivaylokenov/C-Sharp-API-Scenarios-REST-GraphQL-gRPC/c3af239b4e2c3de4228e24d14e9a83a18278545b/Presentation.pptx --------------------------------------------------------------------------------