diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7c94183 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,86 @@ +[*.cs] + +# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. +dotnet_diagnostic.CS8618.severity = none +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = one_less_than_current +csharp_space_around_binary_operators = before_and_after + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf diff --git a/README.md b/README.md index 58b28a7..df3b8a5 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,10 @@ ASP.NET Core 6/7 MVC & EF (Code-First) (HttpGet/Post/Put/Delete) Swagger Azure A ******************************************************** STEPS (Swagger Azure AD Autht - Swagger Azure AD Login)= ******************************************************** +![Authorized Page](https://user-images.githubusercontent.com/57094137/210284591-4a4d7eaa-275f-49ea-bf0a-a87bdf80f35f.jpg) -Opening programs = --------------------------------------- +Open programs = +----------------------------------- Open Visual Studio/-Code. After that Open your project/api (Exp.=StudentApp) @@ -16,6 +17,9 @@ Creating Swagger Azure AD Api = Now we are in project/api in Visual Studio/-Code, - On Connected services/Solution Explorer click right & add Microsoft Identity Platform, + +![ConnectedServices-Microsoft Identity Platform](https://user-images.githubusercontent.com/57094137/210285547-d47ebdbb-f02a-488c-8fec-3328709199b9.jpg) + - On the new opened page if you are not login to Azure login/sign in to Azure, - On the new opened page after logging in you will see your created App's name as a list, - We are still on this page, on the upper right click (+) to create/register application, @@ -51,17 +55,21 @@ Making changes on project using data from Azure Portal = - this data which are "Application (client) id, Directory (tenant) id etc..." is created automatically when creating SwaggerAzureADApi and uploaded to appsettings.json. +![SwaggerAzureADApi App regs](https://user-images.githubusercontent.com/57094137/210285097-b0dc9055-def3-4d04-a382-feeca68fd0c0.jpg) -RUN project whether it is working or not = +RUN project to check it is working or not = ------------------------------------------ - run get method and execute, - if you get response as "Status Code 401", it is okay and we need to make some changes in Program.cs. +![Error 401](https://user-images.githubusercontent.com/57094137/210284937-710c53ce-bfda-4620-b65b-b09d291b90a0.jpg) Making changes on Program.cs/StudentApp = ----------------------------------------- - go to "builder.Services.AddSwaggerGen();" and add these codes to add OpenApiInfo & OpenApiSecurityScheme: +![AddSwaggerGen](https://user-images.githubusercontent.com/57094137/210285353-71bc8838-238f-4d49-8c1f-901f82e43c4b.jpg) + builder.Services.AddSwaggerGen( c=> { @@ -119,6 +127,8 @@ Making changes on project/StudentApp = -------------------------------------- - go to appsettings.json and add these codes from program.cs following "AzureAd" : +![VS App regs](https://user-images.githubusercontent.com/57094137/210285156-39e8a2ef-de27-4434-a763-d8705256eaf6.jpg) + "AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "11august22.onmicrosoft.com", @@ -129,23 +139,23 @@ Making changes on project/StudentApp = }, "SwaggerAzureAD": { "AuthorizationUrl": "https://login.microsoftonline.com/d5af55b1-09d1-4c62-b91b-d108fd981704/oauth2/v2.0/authorize", - >>>> take this data from "SwaggerClientAppRegistration | Endpoints" (authorization endpoint(v2)) + >> take this data from "SwaggerClientAppRegistration | Endpoints" (authorization endpoint(v2)) "TokenUrl": "https://login.microsoftonline.com/d5af55b1-09d1-4c62-b91b-d108fd981704/oauth2/v2.0/token", - >>>> take this data from "SwaggerClientAppRegistration | Endpoints" (token endpoint(v2)) + >> take this data from "SwaggerClientAppRegistration | Endpoints" (token endpoint(v2)) "Scope": "api://a3f042ab-660c-4b16-92ee-6c1ea013f75e/access_as_user", - >>>> go to "SwaggerClientAppRegistration | API permissions", - >>>> click (+ Add a Permission) on the new page click "My APIs", - >>>> click project api name (exp. SwaggerAzureADApi) on the new page click "access_as_user", - >>>> on the same page click (Add permissions) - >>>> we are again on the "SwaggerClientAppRegistration | API permissions" page, - >>>> click "access_as_user" and on the new page copy data "api://......................." - >>>> paste this data to "Scope". + >> go to "SwaggerClientAppRegistration | API permissions", + >> click (+ Add a Permission) on the new page click "My APIs", + >> click project api name (exp. SwaggerAzureADApi) on the new page click "access_as_user", + >> on the same page click (Add permissions) + >> we are again on the "SwaggerClientAppRegistration | API permissions" page, + >> click "access_as_user" and on the new page copy data "api://......................." + >> paste this data to "Scope". "ClientId": "e6e468b6-e2bd-405f-8808-40aef44b49da" - >>>> take this data from "SwaggerClientAppRegistration | Overview" section (Application (client) ID) + >> take this data from "SwaggerClientAppRegistration | Overview" section (Application (client) ID) } @@ -168,21 +178,26 @@ Making changes on Azure Portal = - click (+ Add a platform) - on the new page click ("Single-page application"), - on the new opened page click ("Access tokens" and "ID tokens") but don't save because we need "Redirect URIs", -- so NOW, RUN program from Visual Studio/-code, !!! +- NOW, RUN program from Visual Studio/-code, !!! and on the opened page copy the url (exp."https://localhost:7019/swagger") - and paste it to "Redirect URIs" as (exp."https://localhost:7019/swagger/oauth2-redirect.html"), - now you can click save/configure "Configure single-page application". Dont close RUNNING program. !!! -GO BACK TO Running program, Swagger page= ----------------------------------------- +BACK TO Running program, Swagger page= +-------------------------------------- - Click ("Authorize") button, - on the new page click data ("api://...................") under "Scopes" and then click "Authorize" button. - on the new opened page Sign in with your account (account logging information in which you created SwaggerAzureADApi) - on the new page give permission and click "Accept", - now WE GOT TOKEN. + +![Got Token](https://user-images.githubusercontent.com/57094137/210284617-5a4dd58f-d2f8-4c34-9fa5-ba21c21aa178.jpg) + - TURN TO running program, swagger page, - RUN Methods (get/post/put/update/delete) - NOW WE HAVE ACCESS TO DATA, GETTING RESPONSE, AND METHODS WORKING !!!!! :)) +![Success 200](https://user-images.githubusercontent.com/57094137/210285257-fdc3b299-1be5-4d9f-9274-0072d637664e.jpg) + ******************************************************************************* diff --git a/StudentApp-1.0.sln b/StudentApp-1.0.sln index 28d9b62..15b963f 100644 --- a/StudentApp-1.0.sln +++ b/StudentApp-1.0.sln @@ -3,7 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33110.190 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StudentApp", "StudentApp\StudentApp.csproj", "{B42F1925-CAD9-41DE-877E-ECFDE1FB29FA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StudentApp", "StudentApp\StudentApp.csproj", "{B42F1925-CAD9-41DE-877E-ECFDE1FB29FA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BF9E2E4-6976-4061-AF81-649048F33541}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/StudentApp/AzureStorage/IStorageService.cs b/StudentApp/AzureStorage/IStorageService.cs new file mode 100644 index 0000000..665198c --- /dev/null +++ b/StudentApp/AzureStorage/IStorageService.cs @@ -0,0 +1,7 @@ + +namespace StudentApp.AzureStorage; + +public interface IStorageService +{ + void Upload(IFormFile formFile); +}; \ No newline at end of file diff --git a/StudentApp/AzureStorage/StorageService.cs b/StudentApp/AzureStorage/StorageService.cs new file mode 100644 index 0000000..10a7ece --- /dev/null +++ b/StudentApp/AzureStorage/StorageService.cs @@ -0,0 +1,25 @@ +using Azure.Storage.Blobs; +using StudentApp.Configurations; + +namespace StudentApp.AzureStorage; + +public class StorageService : IStorageService +{ + private readonly BlobServiceClient _blobServiceClient; + private readonly IConfiguration _configuration; + + public StorageService(BlobServiceClient blobServiceClient, IConfiguration configuration) + { + _blobServiceClient = blobServiceClient; + _configuration = configuration; + } + + public void Upload(IFormFile formFile) + { + var containerName = _configuration.Get().Storage.ContainerName; + var containerClient = _blobServiceClient.GetBlobContainerClient(containerName); + var blobClient = containerClient.GetBlobClient(formFile.FileName); + using var stream = formFile.OpenReadStream(); + blobClient.Upload(stream, true); + } +} diff --git a/StudentApp/Configurations/AppConfig.cs b/StudentApp/Configurations/AppConfig.cs new file mode 100644 index 0000000..88c71ec --- /dev/null +++ b/StudentApp/Configurations/AppConfig.cs @@ -0,0 +1,11 @@ + +namespace StudentApp.Configurations; + +public class AppConfig +{ + public SqlServer SqlServer { get; set; } + public AzureAd AzureAd { get; set; } + public SwaggerAzureAD SwaggerAzureAD { get; set; } + public AppSettings AppSettings { get; set; } + public Storage Storage { get; set; } +} \ No newline at end of file diff --git a/StudentApp/Configurations/AppSettings.cs b/StudentApp/Configurations/AppSettings.cs new file mode 100644 index 0000000..640f7ff --- /dev/null +++ b/StudentApp/Configurations/AppSettings.cs @@ -0,0 +1,8 @@ + +namespace StudentApp.Configurations; + +public class AppSettings +{ + public string RunPerMinute { get; init; } + public bool IsFakeValue { get; init; } +} diff --git a/StudentApp/Configurations/AzureAd.cs b/StudentApp/Configurations/AzureAd.cs new file mode 100644 index 0000000..ea3a8ba --- /dev/null +++ b/StudentApp/Configurations/AzureAd.cs @@ -0,0 +1,13 @@ + +namespace StudentApp.Configurations; + +public class AzureAd +{ + public string Instance { get; set; } = + "https://login.microsoftonline.com/"; + public string Domain { get; set; } + public string TenantId { get; set; } + public string ClientId { get; set; } + public string CallbackPath { get; set; } + public string Scopes { get; set; } +} diff --git a/StudentApp/Configurations/SqlServer.cs b/StudentApp/Configurations/SqlServer.cs new file mode 100644 index 0000000..223318a --- /dev/null +++ b/StudentApp/Configurations/SqlServer.cs @@ -0,0 +1,7 @@ + +namespace StudentApp.Configurations; + +public class SqlServer +{ + public string StudentAppContext { get; init; } +} diff --git a/StudentApp/Configurations/Storage.cs b/StudentApp/Configurations/Storage.cs new file mode 100644 index 0000000..c330e14 --- /dev/null +++ b/StudentApp/Configurations/Storage.cs @@ -0,0 +1,8 @@ + +namespace StudentApp.Configurations; + +public class Storage +{ + public string ConnectionString { get; init; } + public string ContainerName { get; init; } +} diff --git a/StudentApp/Configurations/SwaggerAzureAD.cs b/StudentApp/Configurations/SwaggerAzureAD.cs new file mode 100644 index 0000000..6bd8c02 --- /dev/null +++ b/StudentApp/Configurations/SwaggerAzureAD.cs @@ -0,0 +1,11 @@ + +namespace StudentApp.Configurations; + +public class SwaggerAzureAD +{ + public string AuthorizationUrl {get; set; } = + "https://login.microsoftonline.com/d5af55b1-09d1-4c62-b91b-d108fd981704/oauth2/v2.0/authorize"; + public string TokenUrl { get; set;} + public string Scope { get; set;} + public string ClientId { get; set;} +} diff --git a/StudentApp/Controllers/HomeController.cs b/StudentApp/Controllers/HomeController.cs index 5a9d227..af1bbd8 100644 --- a/StudentApp/Controllers/HomeController.cs +++ b/StudentApp/Controllers/HomeController.cs @@ -1,33 +1,30 @@ - -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using StudentApp.Models; using System.Diagnostics; -namespace StudentApp.Controllers +namespace StudentApp.Controllers; +public class HomeController : Controller { - public class HomeController : Controller - { - private readonly ILogger _logger; + private readonly ILogger _logger; - public HomeController(ILogger logger) - { - _logger = logger; - } + public HomeController(ILogger logger) + { + _logger = logger; + } - public IActionResult Index() - { - return View(); - } + public IActionResult Index() + { + return View(); + } - public IActionResult Privacy() - { - return View(); - } + public IActionResult Privacy() + { + return View(); + } - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public IActionResult Error() - { - return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); - } + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } -} \ No newline at end of file +} diff --git a/StudentApp/Controllers/StudentsController.cs b/StudentApp/Controllers/StudentsController.cs index 271fb4f..563fc4e 100644 --- a/StudentApp/Controllers/StudentsController.cs +++ b/StudentApp/Controllers/StudentsController.cs @@ -1,61 +1,93 @@  -using Microsoft.AspNetCore.Authorization; + +using LanguageExt; using Microsoft.AspNetCore.Mvc; -using Microsoft.Identity.Web.Resource; +using StudentApp.AzureStorage; +using StudentApp.Configurations; using StudentApp.Models; using StudentApp.Services; namespace StudentApp.Controllers; -[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")] -[Authorize] +//[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")] +//[Authorize] [ApiController] [Route("[controller]")] public class StudentsController : ControllerBase { private readonly IService _service; - - public StudentsController(IService service) + private readonly IWebHostEnvironment _webHostEnvironment; + private readonly IConfiguration _configuration; + private readonly string _wwwRootPath; + //private readonly IStorageService _storageService; + private readonly ILogger _logger; + public StudentsController(IConfiguration configuration, IService service, IWebHostEnvironment webHostEnvironment, ILogger logger) { _service = service; + _webHostEnvironment = webHostEnvironment; + _configuration = configuration; + _wwwRootPath = _webHostEnvironment.WebRootPath; + //_storageService = storageService; + _logger = logger; } [HttpGet] - public async Task> GetStudent() + public async Task>> GetStudent() { + var students = new List(); var result = await _service.Get(); - return Ok(result); + if (result is null) + { + _logger.LogDebug("Students data not retrieved from the service."); + return Ok(students); + } + else + { + for (var i = 0; i < result.Count; i++) + { + students.Add(new StudentResponse(result[i])); + } + _logger.LogInformation("---------------------------------------------TEST------------------------------------------"); + _logger.LogInformation("Students data retrieved from the service."); + return Ok(students); + } } [HttpGet("GetAsId/")] - public async Task> GetAsId(int id) + public async Task> GetAsId(int id) { var result = await _service.GetAsId(id); if (result == null) + { + _logger.LogWarning($"Wrong student id:{id}."); return NotFound($"Wrong Id {id}"); - return Ok(result); + } + return Ok(new StudentResponse(result)); } [HttpPost] - public async Task> AddStudent(AddStudentRequest addStudentRequest) + public async Task> AddStudent(AddStudentRequest addStudentRequest) { var request = new AddStudentRequest(); var student = request.ToStudent(addStudentRequest); var result = await _service.AddStudent(student); - return Ok(new AddStudentRequest(result)); + return Ok(new StudentResponse(result)); } [HttpPut] [Route("{id}")] - public async Task> UpdateStudent(int id, UpdateStudentRequest updateStudentRequest) + public async Task> UpdateStudent(int id, UpdateStudentRequest updateStudentRequest) { var request = new UpdateStudentRequest(); - var student = request.UpdateStudent(updateStudentRequest); + var student = request.ToUpdateStudent(updateStudentRequest); var result = await _service.UpdateStudent(id, student); if (result == null) + { + _logger.LogWarning($"Wrong student id:{id}."); return NotFound($"Wrong Id {id}"); - return Ok(new UpdateStudentRequest(result)); + } + return Ok(new StudentResponse(result)); } [HttpDelete] @@ -63,8 +95,131 @@ public async Task> UpdateStudent(int id, Upda public async Task> DeleteStudent(int id) { var result = await _service.DeleteStudent(id); - if (result == null) - return NotFound($"False: Wrong Id {id}"); + if (result == null) + { + _logger.LogWarning($"Wrong student id:{id}."); + return NotFound($"False: Wrong Id {id}"); + } + if (result == false) + { + _logger.LogError($"Deleting student id:{id} is NOT successful"); + return NotFound($"False: Deleting Id {id} is NOT successful"); + } return Ok($"True: Deleting Id {id} is successful"); } + + //UploadImage + [HttpPut("UploadImage")] + public async Task UploadImage(IFormFile uploadedFile, int studentId, int imageId) + { + //student id check + var result = await _service.GetAsId(studentId); + if (result == null) + { + _logger.LogWarning($"Wrong student id:{studentId}."); + return NotFound($"Wrong studentId: {studentId}"); + } + try + { + //_storageService.Upload(uploadedFile); + + //Save image to wwwroot/image + _logger.LogInformation("Creating path for file."); + string path = Path.Combine(_wwwRootPath + "\\Image\\", uploadedFile.FileName); + + //image id check + var imageIdContext = result.ImageStudent.ToList().Find(i => i.StudentsId == studentId).ImageId; + if (imageIdContext != imageId) + { + _logger.LogWarning("Wrong image id:{imageId}", imageId); + return NotFound($"Wrong imageId: {imageId}"); + } + result.ImageStudent.ToList().Find(i => i.ImageId == imageId).ImageName = uploadedFile.FileName; + result.ImageStudent.ToList().Find(i => i.ImageId == imageId).Path = path; + + //Save FileName and path to Db + await _service.UpdateStudent(studentId, result); + + + using var stream = new MemoryStream(); + await uploadedFile.CopyToAsync(stream); + var byteArray = stream.ToArray(); + + using (var fs = new FileStream(path, FileMode.Create)) + { + using (BinaryWriter bw = new BinaryWriter(fs)) + { + bw.Write(byteArray); + }; + } + + return Ok(new StudentResponse(result)); + } + catch (Exception ex) + { + _logger.LogError(ex, "Uploading image exception."); + return NotFound("false: uploading image is not successful"); + } + } + + [HttpGet("ExportImage/")] + public async Task ExportImage(int studentId, int imageId) + { + string contentType = "image/jpg"; + + //student id check + var result = await _service.GetAsId(studentId); + if(result == null) + { + _logger.LogWarning("Wrong student id:{studentId}", studentId); + return NotFound($"studentId: {studentId}, content-type: {contentType}"); + } + + try + { + //image id check + var imageIdContext = result.ImageStudent.ToList().Find(i => i.StudentsId == studentId).ImageId; + if (imageIdContext != imageId) + { + if (_configuration.Get().AppSettings.IsFakeValue) + { + return File(System.IO.File.OpenRead(Path.Combine(_wwwRootPath + "\\Image\\", "1n.jpg")), "image/jpeg"); + } + return BadRequest($"Wrong imageId:{imageId}"); + } + var name = result.ImageStudent.ToList().Find(i => i.ImageId == imageId).ImageName; + var path = result.ImageStudent.ToList().Find(i => i.ImageId == imageId).Path; + + //display image in swagger screen + var imageFileStream = System.IO.File.OpenRead(path); + return File(imageFileStream, "image/jpeg"); + + //BlobContainerClient – Download File or Blob from Azure + + //1.Get a connectionString + //string connectionString = configuration["BlobConnectionString"]; + + //2.Get a blobName + //string blobName = configuration["BlobName"]; + + //3.Get a containerName + //string containerName = configuration["ContainerName"]; + + //4.Get a reference to a container (f.exp. name = "imagefilecontainer") + //BlobContainerClient container = new BlobContainerClient(connectionString, containerName); + + //5.Get a reference to a blob + //BlobClient blob = container.GetBlobClient(blobName); + + //6.Download file to a given path from Azure storage + //await blob.DownloadToAsync(downloadPath); + + + } + catch (Exception ex) + { + _logger.LogError(ex, "Exporting image exception."); + return NotFound("false: exporting image is not successful"); + } + } } \ No newline at end of file diff --git a/StudentApp/Controllers/Validations/IsNotNullOrEmptyAttribute.cs b/StudentApp/Controllers/Validations/IsNotNullOrEmptyAttribute.cs new file mode 100644 index 0000000..ea6b0e8 --- /dev/null +++ b/StudentApp/Controllers/Validations/IsNotNullOrEmptyAttribute.cs @@ -0,0 +1,8 @@ + +using System.ComponentModel.DataAnnotations; + +namespace StudentApp.Controllers.Validations; +public class IsNotNullOrEmptyAttribute : ValidationAttribute +{ + public override bool IsValid(object? value) => value is not null && !value.Equals(string.Empty); +} \ No newline at end of file diff --git a/StudentApp/Controllers/Validations/ValidateJoinDateAttribute.cs b/StudentApp/Controllers/Validations/ValidateJoinDateAttribute.cs new file mode 100644 index 0000000..f05daef --- /dev/null +++ b/StudentApp/Controllers/Validations/ValidateJoinDateAttribute.cs @@ -0,0 +1,31 @@ + +using System.ComponentModel.DataAnnotations; +using StudentApp.Models; + +namespace StudentApp.Controllers.Validations; +public class ValidateAddJoinDateAttribute : ValidationAttribute +{ + public new string ErrorMessage = "Registration date can not be greater than current date"; + protected override ValidationResult IsValid(object? value, ValidationContext validationContext) + { + var model = (AddStudentRequest)validationContext.ObjectInstance; + + if (model.RegistrationDate < DateTime.UtcNow) return ValidationResult.Success; + + return new ValidationResult(ErrorMessage); + } +} + +public class ValidateUpdateJoinDateAttribute : ValidationAttribute +{ + public new string ErrorMessage = "Registration date can not be greater than current date"; + protected override ValidationResult IsValid(object? value, ValidationContext validationContext) + { + var model = (UpdateStudentRequest)validationContext.ObjectInstance; + + if (model.RegistrationDate < DateTime.UtcNow) return ValidationResult.Success; + + return new ValidationResult(ErrorMessage); + } +} + diff --git a/StudentApp/Data/StudentAppContext.cs b/StudentApp/Data/StudentAppContext.cs index 36dda08..74ce820 100644 --- a/StudentApp/Data/StudentAppContext.cs +++ b/StudentApp/Data/StudentAppContext.cs @@ -2,20 +2,55 @@ using Microsoft.EntityFrameworkCore; using StudentApp.Models; -namespace StudentApp.Data +namespace StudentApp.Data; +public class StudentAppContext : DbContext { - public class StudentAppContext : DbContext + public StudentAppContext() + { + } + + public StudentAppContext(DbContextOptions options) + : base(options) + { + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=StudentApp.Data;Trusted_Connection=True"); + } + + public DbSet Student { get; set; } + public DbSet StudentPhoneNo { get; set; } + public DbSet StudentEmailAddress { get; set; } + public DbSet StudentAddress { get; set; } + public DbSet StudentImage { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) { - public StudentAppContext() { - } + modelBuilder.Entity() + .HasOne(e => e.Students) + .WithMany(d => d.PhoneStudent) + .HasForeignKey(e => e.StudentsId) + .IsRequired(false); - public StudentAppContext(DbContextOptions options) - : base(options) - { - } + modelBuilder.Entity() + .HasOne(e => e.Students) + .WithMany(d => d.EmailAddressStudent) + .HasForeignKey(e => e.StudentsId) + .IsRequired(false); - public DbSet Student { get; set; } - public object Students { get; set; } + modelBuilder.Entity() + .HasOne(e => e.Students) + .WithMany(d => d.AddressStudent) + .HasForeignKey(e => e.StudentsId) + .IsRequired(false); + + modelBuilder.Entity() + .HasOne(e => e.Students) + .WithMany(d => d.ImageStudent) + .HasForeignKey(e => e.StudentsId) + .IsRequired(false); + } } } diff --git a/StudentApp/GrpcService/MessageGrpcService.cs b/StudentApp/GrpcService/MessageGrpcService.cs new file mode 100644 index 0000000..6c44a0e --- /dev/null +++ b/StudentApp/GrpcService/MessageGrpcService.cs @@ -0,0 +1,258 @@ +using Azure; +using Grpc.Core; +using GrpcMessage; +using LanguageExt; +using StudentApp.Models; +using StudentApp.Services; +using AddStudentRequest = StudentApp.Models.AddStudentRequest; +using UpdateStudentRequest = StudentApp.Models.UpdateStudentRequest; + +namespace StudentApp.GrpcService; + +public class MessageGrpcService : GrpcMessage.Message.MessageBase +{ + private readonly IService _service; + private readonly ILogger _logger; + public MessageGrpcService(IService service, ILogger logger) + { + _service = service; + _logger = logger; + } + + public override async Task GetAllStudents(GrpcMessage.GetAllStudentsRequest request, IServerStreamWriter responseStream, ServerCallContext context) + { + var result = await _service.Get(); + + if (result is null) + { + _logger.LogDebug("Students data not retrieved from the service."); + await responseStream.WriteAsync(new GrpcMessage.GetAllStudentsResponse()); + } + else + { + for (var i = 0; i < result.Count; i++) + { + var response = new GrpcMessage.GetAllStudentsResponse + { + Id = result[i].StudentId, + FirstName = result[i].FirstName, + SecondName = result[i].SecondName, + LastName = result[i].LastName, + UserName = result[i].UserName, + School = result[i].School + }; + result[i].PhoneStudent.ToList().ForEach(ps => + { + var phone = ps.PhoneNo; + if (phone is not null) + response.PhoneNumber.Add(new PhoneNo() { PhoneNumber = phone ?? string.Empty }); + }); + result[i].EmailAddressStudent.ToList().ForEach(ea => + { + var emailAddress = ea.EmailAddress; + if (emailAddress is not null) + response.EmailAddress.Add(new Email() { EmailAddress = emailAddress ?? string.Empty }); + }); + result[i].AddressStudent.ToList().ForEach(ast => + { + var addressStudent = ast.Address; + if (addressStudent is not null) + response.StudentAddress.Add(new Address() { StudentAddress = addressStudent ?? string.Empty}); + }); + result[i].ImageStudent.ToList().ForEach(ist => + { + var imageStudent = ist.ImageName; + if (imageStudent is not null) + response.StudentImage.Add(new Image() { StudentImage = imageStudent ?? string.Empty }); + }); + + await responseStream.WriteAsync(response); + } + } + } + + public override async Task GetStudent(GrpcMessage.GetStudentRequest request, IServerStreamWriter responseStream, ServerCallContext context) + { + var result = await _service.GetAsId((int)request.Id); + if (result == null) + { + _logger.LogWarning($"Wrong student id:{request.Id}."); + } + else + { + var response = new GrpcMessage.GetStudentResponse + { + Id = result.StudentId, + FirstName = result.FirstName, + SecondName = result.LastName, + LastName = result.LastName, + UserName = result.UserName, + School = result.School + }; + result.PhoneStudent.ToList().ForEach(ps => + { + var phone = ps.PhoneNo; + if (phone is not null) + response.PhoneNumber.Add(new PhoneNo() { PhoneNumber = phone ?? string.Empty }); + }); + result.EmailAddressStudent.ToList().ForEach(ea => + { + var emailAddress = ea.EmailAddress; + if (emailAddress is not null) + response.EmailAddress.Add(new Email() { EmailAddress = emailAddress ?? string.Empty }); + }); + result.AddressStudent.ToList().ForEach(ast => + { + var addressStudent = ast.Address; + if (addressStudent is not null) + response.StudentAddress.Add(new Address() { StudentAddress = addressStudent ?? string.Empty }); + }); + result.ImageStudent.ToList().ForEach(ist => + { + var imageStudent = ist.ImageName; + if (imageStudent is not null) + response.StudentImage.Add(new Image() { StudentImage = imageStudent ?? string.Empty }); + }); + + await responseStream.WriteAsync(response); + } + } + + public override async Task AddStudent(GrpcMessage.AddStudentRequest request, ServerCallContext context) + { + var requestDB = new AddStudentRequest(); + var phoneList = new List(); + var grpcPhones = request.PhoneNumber; + + for (var i = 0; i < grpcPhones.Count; i++) + { + phoneList.Add(new PhoneStudentRequest( + new StudentPhoneNo + { + PhoneNo = grpcPhones[i].PhoneNumber, + })); + } + + _logger.LogInformation("1"); + + var student = new AddStudentRequest + { + UserName = request.UserName, + FirstName = request.FirstName, + SecondName = request.SecondName, + LastName = request.LastName, + School = request.School, + RegistrationDate = DateTime.UtcNow, + PhoneStudent = phoneList, + AddressStudent = new List(), + ImageStudent = new List(), + EmailAddressStudent = new List() + }; + + _logger.LogInformation("2"); + var studentDB = requestDB.ToStudent(student); + _logger.LogInformation("3"); + var result = await _service.AddStudent(studentDB); + _logger.LogInformation("4"); + if (result == null) + { + _logger.LogWarning("Wrong student data"); + return new AddStudentResponse(); + } + else + { + _logger.LogInformation("Student added in the database"); + return new AddStudentResponse(); + } + } + + public override async Task UpdateStudent(GrpcMessage.UpdateStudentRequest request, ServerCallContext context) + { + var requestDB = new UpdateStudentRequest(); + + var student = new UpdateStudentRequest + { + UserName = request.UserName, + FirstName = request.FirstName, + SecondName = request.SecondName, + LastName = request.LastName, + School = request.School, + RegistrationDate = DateTime.UtcNow, + PhoneStudent = new List(), + AddressStudent = new List(), + ImageStudent = new List(), + EmailAddressStudent = new List() + }; + + var studentDB = requestDB.ToUpdateStudent(student); + + var result = await _service.UpdateStudent((int)request.Id, studentDB); + if (result == null) + { + _logger.LogWarning("Wrong student data"); + return new UpdateStudentResponse(); + } + else + { + _logger.LogInformation("Student updated in the database"); + return new UpdateStudentResponse(); + } + } + + public override async Task DeleteStudent (GrpcMessage.DeleteStudentRequest request, ServerCallContext context) + { + var result = await _service.DeleteStudent((int)request.Id); + if (result == null) + { + _logger.LogWarning($"Wrong student id:{request.Id}."); + return new DeleteStudentResponse(); + } + if (result == false) + { + _logger.LogError($"Deleting student id:{request.Id} is NOT successful"); + return new DeleteStudentResponse(); + } + _logger.LogInformation($"True: Deleting Id {request.Id} is successful"); + return new DeleteStudentResponse(); + } + + //Ex-Grpc Messages + public override async Task GetAllMessages(GrpcMessage.GetAllMessagesRequest request, IServerStreamWriter responseStream, ServerCallContext context) + { + for (var i = 0; i < 10; i++) + { + var response = new GrpcMessage.GetAllMessagesResponse + { + Id = i + 1, + From = "A", + To = "O", + Message = "Napiyon Kanka", + }; + await responseStream.WriteAsync(response); + } + } + + public override async Task GetMessage(GrpcMessage.GetMessageRequest request, ServerCallContext context) + { + _logger.LogInformation($"Requested message id {request.Id}"); + var response = new GrpcMessage.GetMessageResponse + { + Response = new GrpcMessage.GetAllMessagesResponse + { + Id = request.Id, + From = "A", + To = "O", + Message = "Napiyon Kanka", + } + }; + return await Task.FromResult(response); + } + + public override async Task SendMessage(GrpcMessage.SendMessageRequest request, ServerCallContext context) + { + _logger.LogInformation($"Sending message id {request.Id}, from {request.From}, to {request.To}, message {request.Message}"); + return await Task.FromResult(new GrpcMessage.SendMessageResponse { Id = -1 }); + } + +} + diff --git a/StudentApp/Migrations/20221201142435_initdata.Designer.cs b/StudentApp/Migrations/20221201142435_initdata.Designer.cs deleted file mode 100644 index f70ed60..0000000 --- a/StudentApp/Migrations/20221201142435_initdata.Designer.cs +++ /dev/null @@ -1,60 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using StudentApp.Data; - -#nullable disable - -namespace StudentApp.Migrations -{ - [DbContext(typeof(StudentAppContext))] - [Migration("20221201142435_initdata")] - partial class initdata - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("StudentApp.Models.Student", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Address") - .HasColumnType("nvarchar(max)"); - - b.Property("FirstName") - .HasColumnType("nvarchar(max)"); - - b.Property("LastName") - .HasColumnType("nvarchar(max)"); - - b.Property("School") - .HasColumnType("nvarchar(max)"); - - b.Property("TlfNo") - .HasColumnType("nvarchar(max)"); - - b.Property("UserName") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Student"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/StudentApp/Migrations/20221201142435_initdata.cs b/StudentApp/Migrations/20221201142435_initdata.cs deleted file mode 100644 index 47d3196..0000000 --- a/StudentApp/Migrations/20221201142435_initdata.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace StudentApp.Migrations -{ - /// - public partial class initdata : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Student", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserName = table.Column(type: "nvarchar(max)", nullable: true), - FirstName = table.Column(type: "nvarchar(max)", nullable: true), - LastName = table.Column(type: "nvarchar(max)", nullable: true), - Address = table.Column(type: "nvarchar(max)", nullable: true), - TlfNo = table.Column(type: "nvarchar(max)", nullable: true), - School = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Student", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Student"); - } - } -} diff --git a/StudentApp/Migrations/20230612094654_UpdateStudentsTables.Designer.cs b/StudentApp/Migrations/20230612094654_UpdateStudentsTables.Designer.cs new file mode 100644 index 0000000..e20a166 --- /dev/null +++ b/StudentApp/Migrations/20230612094654_UpdateStudentsTables.Designer.cs @@ -0,0 +1,216 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using StudentApp.Data; + +#nullable disable + +namespace StudentApp.Migrations +{ + [DbContext(typeof(StudentAppContext))] + [Migration("20230612094654_UpdateStudentsTables")] + partial class UpdateStudentsTables + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("StudentApp.Models.StudentAddress", b => + { + b.Property("AddressId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("AddressId")); + + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Country") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PostNumber") + .HasColumnType("int"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("AddressId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentAddress"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentEmailAddress", b => + { + b.Property("EmailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("EmailId")); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("EmailId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentEmailAddress"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentImage", b => + { + b.Property("ImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ImageId")); + + b.Property("ImageName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("ImageId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentImage"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentPhoneNo", b => + { + b.Property("PhoneId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("PhoneId")); + + b.Property("PhoneNo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("PhoneId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentPhoneNo"); + }); + + modelBuilder.Entity("StudentApp.Models.Students", b => + { + b.Property("StudentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("StudentId")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RegistrationDate") + .HasColumnType("datetime2"); + + b.Property("School") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SecondName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("StudentId"); + + b.ToTable("Student"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentAddress", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("AddressStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentEmailAddress", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("EmailAddressStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentImage", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("ImageStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentPhoneNo", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("PhoneStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.Students", b => + { + b.Navigation("AddressStudent"); + + b.Navigation("EmailAddressStudent"); + + b.Navigation("ImageStudent"); + + b.Navigation("PhoneStudent"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/StudentApp/Migrations/20230612094654_UpdateStudentsTables.cs b/StudentApp/Migrations/20230612094654_UpdateStudentsTables.cs new file mode 100644 index 0000000..4ba3e63 --- /dev/null +++ b/StudentApp/Migrations/20230612094654_UpdateStudentsTables.cs @@ -0,0 +1,152 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace StudentApp.Migrations +{ + /// + public partial class UpdateStudentsTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Student", + columns: table => new + { + StudentId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserName = table.Column(type: "nvarchar(max)", nullable: false), + FirstName = table.Column(type: "nvarchar(max)", nullable: false), + SecondName = table.Column(type: "nvarchar(max)", nullable: false), + LastName = table.Column(type: "nvarchar(max)", nullable: false), + School = table.Column(type: "nvarchar(max)", nullable: false), + RegistrationDate = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Student", x => x.StudentId); + }); + + migrationBuilder.CreateTable( + name: "StudentAddress", + columns: table => new + { + AddressId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Address = table.Column(type: "nvarchar(max)", nullable: false), + City = table.Column(type: "nvarchar(max)", nullable: false), + PostNumber = table.Column(type: "int", nullable: false), + Country = table.Column(type: "nvarchar(max)", nullable: false), + StudentsId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_StudentAddress", x => x.AddressId); + table.ForeignKey( + name: "FK_StudentAddress_Student_StudentsId", + column: x => x.StudentsId, + principalTable: "Student", + principalColumn: "StudentId"); + }); + + migrationBuilder.CreateTable( + name: "StudentEmailAddress", + columns: table => new + { + EmailId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + EmailAddress = table.Column(type: "nvarchar(max)", nullable: false), + StudentsId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_StudentEmailAddress", x => x.EmailId); + table.ForeignKey( + name: "FK_StudentEmailAddress_Student_StudentsId", + column: x => x.StudentsId, + principalTable: "Student", + principalColumn: "StudentId"); + }); + + migrationBuilder.CreateTable( + name: "StudentImage", + columns: table => new + { + ImageId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Path = table.Column(type: "nvarchar(max)", nullable: false), + ImageName = table.Column(type: "nvarchar(max)", nullable: false), + StudentsId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_StudentImage", x => x.ImageId); + table.ForeignKey( + name: "FK_StudentImage_Student_StudentsId", + column: x => x.StudentsId, + principalTable: "Student", + principalColumn: "StudentId"); + }); + + migrationBuilder.CreateTable( + name: "StudentPhoneNo", + columns: table => new + { + PhoneId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + PhoneNo = table.Column(type: "nvarchar(max)", nullable: false), + StudentsId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_StudentPhoneNo", x => x.PhoneId); + table.ForeignKey( + name: "FK_StudentPhoneNo_Student_StudentsId", + column: x => x.StudentsId, + principalTable: "Student", + principalColumn: "StudentId"); + }); + + migrationBuilder.CreateIndex( + name: "IX_StudentAddress_StudentsId", + table: "StudentAddress", + column: "StudentsId"); + + migrationBuilder.CreateIndex( + name: "IX_StudentEmailAddress_StudentsId", + table: "StudentEmailAddress", + column: "StudentsId"); + + migrationBuilder.CreateIndex( + name: "IX_StudentImage_StudentsId", + table: "StudentImage", + column: "StudentsId"); + + migrationBuilder.CreateIndex( + name: "IX_StudentPhoneNo_StudentsId", + table: "StudentPhoneNo", + column: "StudentsId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "StudentAddress"); + + migrationBuilder.DropTable( + name: "StudentEmailAddress"); + + migrationBuilder.DropTable( + name: "StudentImage"); + + migrationBuilder.DropTable( + name: "StudentPhoneNo"); + + migrationBuilder.DropTable( + name: "Student"); + } + } +} diff --git a/StudentApp/Migrations/StudentAppContextModelSnapshot.cs b/StudentApp/Migrations/StudentAppContextModelSnapshot.cs index 6c81d52..7a967b2 100644 --- a/StudentApp/Migrations/StudentAppContextModelSnapshot.cs +++ b/StudentApp/Migrations/StudentAppContextModelSnapshot.cs @@ -1,6 +1,9 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using StudentApp.Data; #nullable disable @@ -19,36 +22,191 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - modelBuilder.Entity("StudentApp.Models.Student", b => + modelBuilder.Entity("StudentApp.Models.StudentAddress", b => { - b.Property("Id") + b.Property("AddressId") .ValueGeneratedOnAdd() .HasColumnType("int"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("AddressId")); b.Property("Address") + .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("City") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Country") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PostNumber") + .HasColumnType("int"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("AddressId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentAddress"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentEmailAddress", b => + { + b.Property("EmailId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("EmailId")); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("EmailId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentEmailAddress"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentImage", b => + { + b.Property("ImageId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ImageId")); + + b.Property("ImageName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("ImageId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentImage"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentPhoneNo", b => + { + b.Property("PhoneId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("PhoneId")); + + b.Property("PhoneNo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StudentsId") + .HasColumnType("int"); + + b.HasKey("PhoneId"); + + b.HasIndex("StudentsId"); + + b.ToTable("StudentPhoneNo"); + }); + + modelBuilder.Entity("StudentApp.Models.Students", b => + { + b.Property("StudentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("StudentId")); + b.Property("FirstName") + .IsRequired() .HasColumnType("nvarchar(max)"); b.Property("LastName") + .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("RegistrationDate") + .HasColumnType("datetime2"); + b.Property("School") + .IsRequired() .HasColumnType("nvarchar(max)"); - b.Property("TlfNo") + b.Property("SecondName") + .IsRequired() .HasColumnType("nvarchar(max)"); b.Property("UserName") + .IsRequired() .HasColumnType("nvarchar(max)"); - b.HasKey("Id"); + b.HasKey("StudentId"); b.ToTable("Student"); }); + + modelBuilder.Entity("StudentApp.Models.StudentAddress", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("AddressStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentEmailAddress", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("EmailAddressStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentImage", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("ImageStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.StudentPhoneNo", b => + { + b.HasOne("StudentApp.Models.Students", "Students") + .WithMany("PhoneStudent") + .HasForeignKey("StudentsId"); + + b.Navigation("Students"); + }); + + modelBuilder.Entity("StudentApp.Models.Students", b => + { + b.Navigation("AddressStudent"); + + b.Navigation("EmailAddressStudent"); + + b.Navigation("ImageStudent"); + + b.Navigation("PhoneStudent"); + }); #pragma warning restore 612, 618 } } diff --git a/StudentApp/Models/AddStudentRequest.cs b/StudentApp/Models/AddStudentRequest.cs index 0266882..9e9698b 100644 --- a/StudentApp/Models/AddStudentRequest.cs +++ b/StudentApp/Models/AddStudentRequest.cs @@ -1,39 +1,52 @@ -namespace StudentApp.Models + +using StudentApp.Controllers.Validations; + +namespace StudentApp.Models; +public class AddStudentRequest { - public class AddStudentRequest + public AddStudentRequest() { - public AddStudentRequest() - { - } - public AddStudentRequest(Students students) - { - UserName= students.UserName; - FirstName= students.FirstName; - LastName= students.LastName; - Address= students.Address; - TlfNo= students.TlfNo; - School= students.School; - } + } + public AddStudentRequest(Students students) + { + UserName= students.UserName??"Test"; + FirstName= students.FirstName; + SecondName = students.SecondName; + LastName= students.LastName; + School= students.School; - public string? UserName { get; set; } - public string? FirstName { get; set; } - public string? LastName { get; set; } + RegistrationDate = students.RegistrationDate; + PhoneStudent = students.PhoneStudent.Select(p => new PhoneStudentRequest(p)).ToList(); + EmailAddressStudent = students.EmailAddressStudent.Select(p => new EmailAddressStudentRequest(p)).ToList(); + AddressStudent = students.AddressStudent.Select(p => new AddressStudentRequest(p)).ToList(); + ImageStudent = students.ImageStudent.Select(p => new ImageStudentRequest(p)).ToList(); + } - public string? Address { get; set; } - public string? TlfNo { get; set; } - public string? School { get; set; } + [IsNotNullOrEmpty] public string UserName { get; set; } + [IsNotNullOrEmpty] public string FirstName { get; set; } + [IsNotNullOrEmpty] public string SecondName { get; set; } + [IsNotNullOrEmpty] public string LastName { get; set; } + [IsNotNullOrEmpty] public string School { get; set; } + [ValidateAddJoinDate] public DateTime RegistrationDate { get; set; } + [IsNotNullOrEmpty] public ICollection PhoneStudent { get; set; } + [IsNotNullOrEmpty] public ICollection EmailAddressStudent { get; set; } + [IsNotNullOrEmpty] public ICollection AddressStudent { get; set; } + [IsNotNullOrEmpty] public ICollection ImageStudent { get; set; } - public virtual Students ToStudent(AddStudentRequest addStudentRequest) + public virtual Students ToStudent(AddStudentRequest addStudentRequest) + { + return new Students { - return new Students - { - UserName = addStudentRequest.UserName, - FirstName = addStudentRequest.FirstName, - LastName = addStudentRequest.LastName, - Address = addStudentRequest.Address, - TlfNo = addStudentRequest.TlfNo, - School = addStudentRequest.School - }; - } + UserName = addStudentRequest.UserName, + FirstName = addStudentRequest.FirstName, + SecondName = addStudentRequest.SecondName, + LastName = addStudentRequest.LastName, + School = addStudentRequest.School, + RegistrationDate = addStudentRequest.RegistrationDate, + PhoneStudent = addStudentRequest.PhoneStudent.Select(p => p.ToPhoneStudent()).ToList(), + EmailAddressStudent = addStudentRequest.EmailAddressStudent.Select(e => e.ToEmailStudent()).ToList(), + AddressStudent = addStudentRequest.AddressStudent.Select(p => p.ToAddressStudent()).ToList(), + ImageStudent = addStudentRequest.ImageStudent.Select(p => p.ToImageStudent()).ToList() + }; } } diff --git a/StudentApp/Models/AddressStudentRequest.cs b/StudentApp/Models/AddressStudentRequest.cs new file mode 100644 index 0000000..6a95950 --- /dev/null +++ b/StudentApp/Models/AddressStudentRequest.cs @@ -0,0 +1,34 @@ + +using StudentApp.Controllers.Validations; + +namespace StudentApp.Models; +public class AddressStudentRequest{ + public AddressStudentRequest() { } + public AddressStudentRequest(ICollection addressStudent) { } + public AddressStudentRequest(StudentAddress studentAddress){ + Address = studentAddress.Address; + City = studentAddress.City; + PostNumber = studentAddress.PostNumber; + Country= studentAddress.Country;} + + [IsNotNullOrEmpty] public string Address { get; set; } + [IsNotNullOrEmpty] public string City { get; set; } + [IsNotNullOrEmpty] public int PostNumber { get; set; } + [IsNotNullOrEmpty] public string Country { get; set; } + + public virtual StudentAddress ToAddressStudent(AddressStudentRequest addressStudentRequest){ + return new StudentAddress{ + Address= addressStudentRequest.Address, + City = addressStudentRequest.City, + PostNumber = addressStudentRequest.PostNumber, + Country = addressStudentRequest.Country + };} + + public StudentAddress ToAddressStudent(){ + return new StudentAddress{ + Address = Address, + City = City, + PostNumber = PostNumber, + Country = Country + };} +} diff --git a/StudentApp/Models/EmailAddressStudentRequest.cs b/StudentApp/Models/EmailAddressStudentRequest.cs new file mode 100644 index 0000000..2693c2f --- /dev/null +++ b/StudentApp/Models/EmailAddressStudentRequest.cs @@ -0,0 +1,29 @@ + +using StudentApp.Controllers.Validations; + +namespace StudentApp.Models; +public class EmailAddressStudentRequest +{ + public EmailAddressStudentRequest() + { + } + + public EmailAddressStudentRequest(ICollection emailAddressStudent) + { + } + public EmailAddressStudentRequest(StudentEmailAddress studentEmailAddress) + { + EmailAddress = studentEmailAddress.EmailAddress; + } + [IsNotNullOrEmpty] public string EmailAddress { get; set; } + + public virtual StudentEmailAddress ToEmailStudent(EmailAddressStudentRequest emailAddressStudentRequest) + { + return new StudentEmailAddress + { + EmailAddress = emailAddressStudentRequest.EmailAddress + }; + } + + public StudentEmailAddress ToEmailStudent() => new StudentEmailAddress { EmailAddress = EmailAddress }; +} diff --git a/StudentApp/Models/ErrorViewModel.cs b/StudentApp/Models/ErrorViewModel.cs index eedb0c2..43b5402 100644 --- a/StudentApp/Models/ErrorViewModel.cs +++ b/StudentApp/Models/ErrorViewModel.cs @@ -1,10 +1,8 @@ -namespace StudentApp.Models +namespace StudentApp.Models; +public class ErrorViewModel { - public class ErrorViewModel - { - public string? RequestId { get; set; } + public string? RequestId { get; set; } - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - } -} \ No newline at end of file + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); +} diff --git a/StudentApp/Models/ImageStudentRequest.cs b/StudentApp/Models/ImageStudentRequest.cs new file mode 100644 index 0000000..c616491 --- /dev/null +++ b/StudentApp/Models/ImageStudentRequest.cs @@ -0,0 +1,39 @@ + +using StudentApp.Controllers.Validations; + +namespace StudentApp.Models; + +public class ImageStudentRequest +{ + + public ImageStudentRequest() {} + + public ImageStudentRequest(StudentImage studentImage) + { + ImageId = studentImage.ImageId; + ImageName = studentImage.ImageName; + Path = studentImage.Path; + } + + [IsNotNullOrEmpty] public int ImageId { get; set; } + [IsNotNullOrEmpty] public string ImageName { get; set; } + [IsNotNullOrEmpty] public string Path { get; set; } + + public virtual StudentImage ToImageStudent(ImageStudentRequest imageStudentRequest) + { + return new StudentImage + { + ImageName = imageStudentRequest.ImageName, + Path = imageStudentRequest.Path + }; + } + + public StudentImage ToImageStudent() + { + return new StudentImage + { + ImageName = ImageName, + Path = Path + }; + } +} \ No newline at end of file diff --git a/StudentApp/Models/PhoneStudentRequest.cs b/StudentApp/Models/PhoneStudentRequest.cs new file mode 100644 index 0000000..39a7b82 --- /dev/null +++ b/StudentApp/Models/PhoneStudentRequest.cs @@ -0,0 +1,17 @@ + +using StudentApp.Controllers.Validations; + +namespace StudentApp.Models; +public class PhoneStudentRequest +{ + public PhoneStudentRequest() + { + } + public PhoneStudentRequest(StudentPhoneNo studentPhone) + { + PhoneNo = studentPhone.PhoneNo; + } + [IsNotNullOrEmpty] public string PhoneNo { get; set; } + + public StudentPhoneNo ToPhoneStudent() => new StudentPhoneNo { PhoneNo = PhoneNo }; +} diff --git a/StudentApp/Models/StudentResponse.cs b/StudentApp/Models/StudentResponse.cs new file mode 100644 index 0000000..d13add4 --- /dev/null +++ b/StudentApp/Models/StudentResponse.cs @@ -0,0 +1,11 @@ + +namespace StudentApp.Models; +public class StudentResponse : AddStudentRequest +{ + public StudentResponse(Students students) : base(students) + { + StudentId = students.StudentId; + } + + public int StudentId { get; set;} +} diff --git a/StudentApp/Models/Students.cs b/StudentApp/Models/Students.cs index 89e7471..d0d1413 100644 --- a/StudentApp/Models/Students.cs +++ b/StudentApp/Models/Students.cs @@ -1,18 +1,63 @@  -namespace StudentApp.Models +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace StudentApp.Models; +public class Students { - public class Students - { - internal int id; + [Key] + public int StudentId { get; set; } + public string UserName { get; set; } + public string FirstName { get; set; } + public string SecondName { get; set; } + public string LastName { get; set; } + public string School { get; set; } + public DateTime RegistrationDate { get; internal set; } + public ICollection PhoneStudent { get; set; } + public ICollection EmailAddressStudent { get; set; } + public ICollection AddressStudent { get; set; } + public ICollection ImageStudent { get; set; } +} - public int Id { get; set; } - public string? UserName { get; set; } - public string? FirstName { get; set; } - public string? LastName { get; set; } +public class StudentPhoneNo +{ + [Key] + public int PhoneId { get; set; } + public string PhoneNo { get; set; } + [ForeignKey("Students")] + public int StudentsId { get; set; } + public Students Students { get; set; } +} +public class StudentEmailAddress +{ + [Key] + public int EmailId { get; set; } + public string EmailAddress { get; set; } + [ForeignKey("Students")] + public int StudentsId { get; set; } + public Students Students { get; set; } - public string? Address { get; set; } - public string? TlfNo { get; set; } - public string? School { get; set; } +} +public class StudentAddress +{ + [Key] + public int AddressId { get; set; } + public string Address { get; set; } + public string City { get; set; } + public int PostNumber { get; set; } + public string Country { get; set; } + [ForeignKey("Students")] + public int StudentsId { get; set; } + public Students Students { get; set; } +} +public class StudentImage +{ + [Key] + public int ImageId { get; set; } + public string Path { get; set; } + public string ImageName { get; set; } - } + [ForeignKey("Students")] + public int StudentsId { get; set; } + public Students Students { get; set; } } diff --git a/StudentApp/Models/UpdateStudentRequest.cs b/StudentApp/Models/UpdateStudentRequest.cs index a0c90f5..b65d83e 100644 --- a/StudentApp/Models/UpdateStudentRequest.cs +++ b/StudentApp/Models/UpdateStudentRequest.cs @@ -1,39 +1,51 @@  -namespace StudentApp.Models -{ - public class UpdateStudentRequest { - public UpdateStudentRequest() - { - } - public UpdateStudentRequest(Students students) - { - UserName = students?.UserName??"Test"; - FirstName = students?.FirstName; - LastName = students?.LastName; - Address = students?.Address; - TlfNo = students?.TlfNo; - School = students?.School; - } +using StudentApp.Controllers.Validations; + +namespace StudentApp.Models; +public class UpdateStudentRequest { + public UpdateStudentRequest() + { + } + public UpdateStudentRequest(Students students) + { + UserName = students.UserName ?? "Test"; + FirstName = students.FirstName; + SecondName = students.SecondName; + LastName = students.LastName; + School = students.School; + RegistrationDate = students.RegistrationDate; + PhoneStudent = students.PhoneStudent.Select(p => new PhoneStudentRequest(p)).ToList(); + EmailAddressStudent = students.EmailAddressStudent.Select(p => new EmailAddressStudentRequest(p)).ToList(); + AddressStudent = students.AddressStudent.Select(p => new AddressStudentRequest(p)).ToList(); + ImageStudent = students.ImageStudent.Select(p => new ImageStudentRequest(p)).ToList(); + } - public string? UserName { get; set; } - public string? FirstName { get; set; } - public string? LastName { get; set; } + [IsNotNullOrEmpty] public string UserName { get; set; } + [IsNotNullOrEmpty] public string FirstName { get; set; } + [IsNotNullOrEmpty] public string SecondName { get; set; } + [IsNotNullOrEmpty] public string LastName { get; set; } + [IsNotNullOrEmpty] public string School { get; set; } + [ValidateUpdateJoinDate] public DateTime RegistrationDate { get; set; } + [IsNotNullOrEmpty] public ICollection PhoneStudent { get; set; } + [IsNotNullOrEmpty] public ICollection EmailAddressStudent { get; set; } + [IsNotNullOrEmpty] public ICollection AddressStudent { get; set; } + [IsNotNullOrEmpty] public ICollection ImageStudent { get; set; } - public string? Address { get; set; } - public string? TlfNo { get; set; } - public string? School { get; set; } - public virtual Students UpdateStudent(UpdateStudentRequest updateStudentRequest) + public virtual Students ToUpdateStudent(UpdateStudentRequest updateStudentRequest) + { + return new Students { - return new Students - { - UserName = updateStudentRequest.UserName, - FirstName = updateStudentRequest.FirstName, - LastName = updateStudentRequest.LastName, - Address = updateStudentRequest.Address, - TlfNo = updateStudentRequest.TlfNo, - School = updateStudentRequest.School - }; - } + UserName = updateStudentRequest.UserName, + FirstName = updateStudentRequest.FirstName, + SecondName = updateStudentRequest.SecondName, + LastName = updateStudentRequest.LastName, + School = updateStudentRequest.School, + RegistrationDate = updateStudentRequest.RegistrationDate, + PhoneStudent = updateStudentRequest.PhoneStudent.Select(p => p.ToPhoneStudent()).ToList(), + EmailAddressStudent = updateStudentRequest.EmailAddressStudent.Select(p => p.ToEmailStudent()).ToList(), + AddressStudent = updateStudentRequest.AddressStudent.Select(p => p.ToAddressStudent()).ToList(), + ImageStudent = updateStudentRequest.ImageStudent.Select(p => p.ToImageStudent()).ToList() + }; } } diff --git a/StudentApp/Program.cs b/StudentApp/Program.cs index 9100215..940ee97 100644 --- a/StudentApp/Program.cs +++ b/StudentApp/Program.cs @@ -1,27 +1,70 @@ - - +#region using using Microsoft.EntityFrameworkCore; using StudentApp.Data; using StudentApp.Repo; using StudentApp.Services; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Identity.Web; +using StudentApp.GrpcService; using Microsoft.OpenApi.Models; +using System.Text.Json.Serialization; +using StudentApp.Configurations; +using Microsoft.Extensions.Azure; +using StudentApp.AzureStorage; +using Serilog; +#endregion + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions +{ + Args = args, + ApplicationName = typeof(Program).Assembly.FullName, + ContentRootPath = Directory.GetCurrentDirectory(), + EnvironmentName = Environments.Staging, + WebRootPath = "wwwroot" +}); + +#region SeriLog +var logger = new LoggerConfiguration() + .ReadFrom.Configuration(builder.Configuration) + .Enrich.FromLogContext() + //.WriteTo.File() + .WriteTo.Console() + .CreateLogger(); +builder.Logging.ClearProviders(); +builder.Logging.AddSerilog(logger); +#endregion + +#region AppConfig +var config = builder.Configuration.Get(); +#endregion -var builder = WebApplication.CreateBuilder(args); +builder.Services.AddControllers(); builder.Services.AddDbContext(option => - option.UseSqlServer(builder.Configuration.GetConnectionString("StudentAppContext") ?? throw new InvalidOperationException("Connection string 'StudentAppContext' not found."))); + option.UseSqlServer(config.SqlServer.StudentAppContext ?? throw new InvalidOperationException("Connection string 'StudentAppContext' not found."))); +//builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) +// .AddMicrosoftIdentityWebApi(config.AzureAd); -builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")); +#region AzureBlob +//builder.Services.AddTransient(); +//builder.Services.AddAzureClients(builder => { builder.AddBlobServiceClient(config.Storage.ConnectionString); }); +#endregion -// Add services to the container. +#region AddingServicesToTheContainer builder.Services.AddControllersWithViews(); builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; + options.JsonSerializerOptions.AllowTrailingCommas = true; + options.JsonSerializerOptions.PropertyNamingPolicy = null; + }); +#endregion + +#region SecureApi builder.Services.AddSwaggerGen( - c=> + c => { - c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title ="Swagger Azure AD Demo", Version= "v1" }); + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Swagger Azure AD Demo", Version = "v1" }); c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "Oauth2.0 which uses AuthorizationCode flow", @@ -31,11 +74,11 @@ { AuthorizationCode = new OpenApiOAuthFlow { - AuthorizationUrl = new Uri(builder.Configuration["SwaggerAzureAD:AuthorizationUrl"]), - TokenUrl = new Uri(builder.Configuration["SwaggerAzureAD:TokenUrl"]), + AuthorizationUrl = new Uri(config.SwaggerAzureAD.AuthorizationUrl), + TokenUrl = new Uri(config.SwaggerAzureAD.TokenUrl), Scopes = new Dictionary { - {builder.Configuration["SwaggerAzureAd:Scope"], "Access API as User"} + {config.SwaggerAzureAD.Scope, "Access API as User"} } } } @@ -47,32 +90,46 @@ { Reference = new OpenApiReference{Type=ReferenceType.SecurityScheme, Id="oauth2"} }, - new []{builder.Configuration["SwaggerAzureAd:Scope"]} + new []{config.SwaggerAzureAD.Scope} } }); }); +#endregion -// Add interfaces (Context) +#region Proto/Grpc +builder.Services.AddGrpc(); +builder.Services.AddGrpcReflection(); +#endregion + +#region Add Interfaces (Context) builder.Services.AddTransient(); builder.Services.AddTransient(); +#endregion var app = builder.Build(); -// Configure the HTTP request pipeline. +//Configure the HTTP request pipeline. +#region Swagger app.UseSwagger(); app.UseSwaggerUI(s => { - s.OAuthClientId(builder.Configuration["SwaggerAzureAd:ClientId"]); + s.OAuthClientId(config.SwaggerAzureAD.ClientId); s.OAuthUsePkce(); s.OAuthScopeSeparator(" "); }); +#endregion + +#region GrpcConfiguration +//app.UseGrpcWeb(); +app.MapGrpcService(); +app.MapGrpcReflectionService(); +#endregion app.UseHttpsRedirection(); -app.UseAuthentication(); +//app.UseAuthentication(); app.UseAuthorization(); -app.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - -app.Run(); +app.MapDefaultControllerRoute(); +//app.MapControllerRoute( +// name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); +app.Run(); \ No newline at end of file diff --git a/StudentApp/Protos/message.proto b/StudentApp/Protos/message.proto new file mode 100644 index 0000000..0e1cc39 --- /dev/null +++ b/StudentApp/Protos/message.proto @@ -0,0 +1,135 @@ +syntax = "proto3"; + +option csharp_namespace = "GrpcMessage"; + +package message; + +service Message { + rpc GetAllStudents (GetAllStudentsRequest) returns (stream GetAllStudentsResponse); + rpc GetStudent (GetStudentRequest) returns (stream GetStudentResponse); + rpc AddStudent (AddStudentRequest) returns (AddStudentResponse); + rpc UpdateStudent (UpdateStudentRequest) returns (UpdateStudentResponse); + rpc DeleteStudent (DeleteStudentRequest) returns (DeleteStudentResponse); + + rpc GetMessage (GetMessageRequest) returns (GetMessageResponse); + rpc SendMessage (SendMessageRequest) returns (SendMessageResponse); + rpc GetAllMessages(GetAllMessagesRequest) returns (stream GetAllMessagesResponse); +} + +message GetAllStudentsRequest {} +message GetAllStudentsResponse +{ + int64 id = 1; + string UserName = 2; + string FirstName = 3; + string SecondName = 4; + string LastName = 5; + string School = 6; + repeated PhoneNo PhoneNumber = 7; + repeated Email EmailAddress = 8; + repeated Address StudentAddress = 9; + repeated Image StudentImage = 10; +} + +message GetStudentRequest +{ + int64 id = 1; +} + +message GetStudentResponse{ + int64 id = 1; + string UserName = 2; + string FirstName = 3; + string SecondName = 4; + string LastName = 5; + string School = 6; + repeated PhoneNo PhoneNumber = 7; + repeated Email EmailAddress = 8; + repeated Address StudentAddress = 9; + repeated Image StudentImage = 10; +} + +message AddStudentRequest +{ + int64 id = 1; + string UserName = 2; + string FirstName = 3; + string SecondName = 4; + string LastName = 5; + string School = 6; + repeated PhoneNo PhoneNumber = 7; + repeated Email EmailAddress = 8; + repeated Address StudentAddress = 9; + repeated Image StudentImage = 10; +} +message AddStudentResponse{} + +message UpdateStudentRequest +{ + int64 id = 1; + string UserName = 2; + string FirstName = 3; + string SecondName = 4; + string LastName = 5; + string School = 6; + repeated PhoneNo PhoneNumber = 7; + repeated Email EmailAddress = 8; + repeated Address StudentAddress = 9; + repeated Image StudentImage = 10; +} +message UpdateStudentResponse {} + +message DeleteStudentRequest +{ + int64 id = 1; +} + +message DeleteStudentResponse{} + + +message PhoneNo +{string PhoneNumber = 1;} + +message Email +{string EmailAddress = 1;} + +message Address +{string StudentAddress = 1;} + +message Image +{string StudentImage = 1;} + + +message GetAllMessagesRequest {} + +message GetAllMessagesResponse +{ + int64 id = 1; + string from = 2; + string to = 3; + string message = 4; +} + +message GetMessageRequest +{ + int64 id = 1; +} + +message GetMessageResponse +{ + GetAllMessagesResponse response = 1; +} + +message SendMessageRequest +{ + int64 id = 1; + string from = 2; + string to = 3; + string message = 4; +} + +message SendMessageResponse +{ + int64 id = 1; +} + diff --git a/StudentApp/Repo/IActionResult.cs b/StudentApp/Repo/IActionResult.cs index c4c093f..3d013d6 100644 --- a/StudentApp/Repo/IActionResult.cs +++ b/StudentApp/Repo/IActionResult.cs @@ -1,6 +1,4 @@ -namespace StudentApp.Repo +namespace StudentApp.Repo; +public interface IActionResult { - public interface IActionResult - { - } } \ No newline at end of file diff --git a/StudentApp/Repo/Repo.cs b/StudentApp/Repo/Repo.cs index ab77f0e..36a25f2 100644 --- a/StudentApp/Repo/Repo.cs +++ b/StudentApp/Repo/Repo.cs @@ -8,17 +8,22 @@ namespace StudentApp.Repo; public class Repo : IRepo { private readonly StudentAppContext _context; + private readonly ILogger _logger; - public Repo(StudentAppContext context) + public Repo(StudentAppContext context, ILogger logger) { _context = context; + _logger = logger; } - public Task> Get() => - _context.Student.ToListAsync(); + public async Task> Get() => + await _context.Student.Include(s => s.AddressStudent) + .Include(s => s.EmailAddressStudent) + .Include(s => s.ImageStudent) + .Include(s => s.PhoneStudent).ToListAsync(); - public async Task GetAsId(int id) => - await _context.Student.FirstOrDefaultAsync(s => s.Id == id); + public async Task GetAsId(int id) => + await _context.Student.Include(s => s.AddressStudent).Include(s => s.EmailAddressStudent).Include(s => s.ImageStudent).Include(s => s.PhoneStudent).FirstOrDefaultAsync(s => s.StudentId == id); public async Task AddStudent(Students students) { @@ -29,15 +34,19 @@ public async Task AddStudent(Students students) public async Task UpdateStudent(int id, Students students) { - var result = await _context.Student.FirstOrDefaultAsync(s => s.Id == id); + var result = await _context.Student.FirstOrDefaultAsync(s => s.StudentId == id); if (result != null) { result.UserName = students.UserName; result.FirstName = students.FirstName; + result.SecondName = students.SecondName; result.LastName = students.LastName; - result.TlfNo = students.TlfNo; result.School = students.School; - result.Address = students.Address; + result.RegistrationDate= students.RegistrationDate; + result.PhoneStudent = students.PhoneStudent; + result.EmailAddressStudent= students.EmailAddressStudent; + result.AddressStudent = students.AddressStudent; + result.ImageStudent = students.ImageStudent; _context.Student.Update(result); await _context.SaveChangesAsync(); @@ -55,21 +64,25 @@ public async Task UpdateStudent(int id, Students students) } try { + //Delete cascade in Db + var addresses = _context.StudentAddress.Find(sa => sa.StudentsId == id).ToList(); + var images = _context.StudentImage.Find(si => si.StudentsId == id).ToList(); + var phones = _context.StudentPhoneNo.Find(sp => sp.StudentsId == id).ToList(); + var mails = _context.StudentEmailAddress.Find(se => se.StudentsId == id).ToList(); + + _context.StudentAddress.RemoveRange(addresses); + _context.StudentImage.RemoveRange(images); + _context.StudentPhoneNo.RemoveRange(phones); + _context.StudentEmailAddress.RemoveRange(mails); + _context.Student.Remove(result); await _context.SaveChangesAsync(); return true; } catch (Exception ex) { + _logger.LogError(ex, "Deleting student data ('db cascade') from context exception."); return false; } } -} - - - - - - - - +} \ No newline at end of file diff --git a/StudentApp/Repo/ValueTask.cs b/StudentApp/Repo/ValueTask.cs index 3bd6934..308d57f 100644 --- a/StudentApp/Repo/ValueTask.cs +++ b/StudentApp/Repo/ValueTask.cs @@ -1,6 +1,4 @@ -namespace StudentApp.Repo +namespace StudentApp.Repo; +internal class ValueTask { - internal class ValueTask - { - } } \ No newline at end of file diff --git a/StudentApp/Services/IService.cs b/StudentApp/Services/IService.cs index 29cc443..bac65b4 100644 --- a/StudentApp/Services/IService.cs +++ b/StudentApp/Services/IService.cs @@ -1,14 +1,12 @@  using StudentApp.Models; -namespace StudentApp.Services +namespace StudentApp.Services; +public interface IService { - public interface IService - { - Task> Get(); - Task GetAsId(int id); - Task AddStudent(Students students); - Task UpdateStudent(int id, Students students); - Task DeleteStudent(int id); - } + Task> Get(); + Task GetAsId(int id); + Task AddStudent(Students students); + Task UpdateStudent(int id, Students students); + Task DeleteStudent(int id); } diff --git a/StudentApp/Services/Service.cs b/StudentApp/Services/Service.cs index 03b09ed..3a3a45b 100644 --- a/StudentApp/Services/Service.cs +++ b/StudentApp/Services/Service.cs @@ -3,7 +3,6 @@ using StudentApp.Repo; namespace StudentApp.Services; - public class Service : IService { @@ -19,4 +18,9 @@ public Service(IRepo repo) public async Task AddStudent(Students students) => await _repo.AddStudent(students); public async Task UpdateStudent(int id, Students students) => await _repo.UpdateStudent(id, students); public async Task DeleteStudent(int id) => await _repo.DeleteStudent(id); + + //public Task UpdateStudent(object id, Students studentDB) + //{ + // throw new NotImplementedException(); + //} } diff --git a/StudentApp/StudentApp.csproj b/StudentApp/StudentApp.csproj index 644aaf5..6cc1c42 100644 --- a/StudentApp/StudentApp.csproj +++ b/StudentApp/StudentApp.csproj @@ -1,16 +1,18 @@ - net6.0 + net7.0 enable enable 36532767-0a66-4c60-8c8d-f74116c147fc + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -20,15 +22,41 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + - + + + - + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + diff --git a/StudentApp/Views/Home/Index.cshtml b/StudentApp/Views/Home/Index.cshtml index d2d19bd..8d580dc 100644 --- a/StudentApp/Views/Home/Index.cshtml +++ b/StudentApp/Views/Home/Index.cshtml @@ -1,8 +1,13 @@ -@{ + +@{ ViewData["Title"] = "Home Page"; }

Welcome

+<<<<< gRPC

Learn about building Web apps with ASP.NET Core.

+

Learn about building Web apps with ASP.NET Core.

+ +>>>>> develop diff --git a/StudentApp/Views/Home/Privacy.cshtml b/StudentApp/Views/Home/Privacy.cshtml index af4fb19..e6eef70 100644 --- a/StudentApp/Views/Home/Privacy.cshtml +++ b/StudentApp/Views/Home/Privacy.cshtml @@ -3,4 +3,4 @@ }

@ViewData["Title"]

-

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

+

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

\ No newline at end of file diff --git a/StudentApp/Views/Shared/_Layout.cshtml b/StudentApp/Views/Shared/_Layout.cshtml index d1336ec..93b348f 100644 --- a/StudentApp/Views/Shared/_Layout.cshtml +++ b/StudentApp/Views/Shared/_Layout.cshtml @@ -13,10 +13,6 @@