| **/.classpath | |||||
| **/.dockerignore | |||||
| **/.env | |||||
| **/.git | |||||
| **/.gitignore | |||||
| **/.project | |||||
| **/.settings | |||||
| **/.toolstarget | |||||
| **/.vs | |||||
| **/.vscode | |||||
| **/*.*proj.user | |||||
| **/*.dbmdl | |||||
| **/*.jfm | |||||
| **/azds.yaml | |||||
| **/bin | |||||
| **/charts | |||||
| **/docker-compose* | |||||
| **/Dockerfile* | |||||
| **/node_modules | |||||
| **/npm-debug.log | |||||
| **/obj | |||||
| **/secrets.dev.yaml | |||||
| **/values.dev.yaml | |||||
| LICENSE | |||||
| README.md |
| [*.cs] | |||||
| # CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. | |||||
| dotnet_diagnostic.CS8618.severity = none |
| ################################################################################ | |||||
| # This .gitignore file was automatically created by Microsoft(R) Visual Studio. | |||||
| ################################################################################ | |||||
| /.vs | |||||
| /Diligent.WebAPI.Data/bin/Debug/net6.0 | |||||
| /Diligent.WebAPI.Data/obj | |||||
| /Diligent.WebAPI.Host/.vs/Diligent.WebAPI/v17 | |||||
| /Diligent.WebAPI.Host/bin/Debug/net6.0 | |||||
| /Diligent.WebAPI.Host/obj | |||||
| /Diligent.WebAPI.Host/Logs | |||||
| /Diligent.WebAPI.Business/bin/Debug/net6.0 | |||||
| /Diligent.WebAPI.Business/obj/Debug/net6.0 | |||||
| /Diligent.WebAPI.Business/obj | |||||
| /Diligent.WebAPI.Contracts/bin/Debug/net6.0 | |||||
| /Diligent.WebAPI.Contracts/obj/Debug/net6.0 | |||||
| /Diligent.WebAPI.Contracts/obj | |||||
| /Diligent.WebAPI.Tests/bin/Debug/net6.0 | |||||
| /Diligent.WebAPI.Tests/obj/Debug/net6.0 | |||||
| /Diligent.WebAPI.Tests/obj | |||||
| /Diligent.WebAPI.Tests/coverageresults |
| <Project Sdk="Microsoft.NET.Sdk"> | |||||
| <PropertyGroup> | |||||
| <TargetFramework>net6.0</TargetFramework> | |||||
| <ImplicitUsings>enable</ImplicitUsings> | |||||
| <Nullable>enable</Nullable> | |||||
| </PropertyGroup> | |||||
| <ItemGroup> | |||||
| <PackageReference Include="AutoMapper" Version="11.0.1" /> | |||||
| <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" /> | |||||
| <PackageReference Include="Azure.Storage.Blobs" Version="12.14.1" /> | |||||
| <PackageReference Include="Bytescout.Spreadsheet" Version="4.6.0.2025" /> | |||||
| <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" /> | |||||
| <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.10" /> | |||||
| <PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="6.0.10" /> | |||||
| <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.10" /> | |||||
| <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.10" /> | |||||
| <PackageReference Include="RestSharp" Version="108.0.2-alpha.0.6" /> | |||||
| <PackageReference Include="WindowsAzure.Storage" Version="9.3.3" /> | |||||
| </ItemGroup> | |||||
| <ItemGroup> | |||||
| <ProjectReference Include="..\Diligent.WebAPI.Contracts\Diligent.WebAPI.Contracts.csproj" /> | |||||
| <ProjectReference Include="..\Diligent.WebAPI.Data\Diligent.WebAPI.Data.csproj" /> | |||||
| </ItemGroup> | |||||
| </Project> |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Business.Extensions | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public static class AdExtensions | |||||
| { | |||||
| public static List<Ad> Filter(this List<Ad> query, AdFilterDto filters) => | |||||
| query.FilterByExperience(filters.MinExperience, filters.MaxExperience).FilterByWorkType(filters.WorkHour).FilterByEmploymentType(filters.EmploymentType).ToList().FilterByTechnologies(filters.Technologies); | |||||
| public static List<Ad> FilterByExperience(this List<Ad> query, int minExperience, int maxExperience) => | |||||
| minExperience >= maxExperience ? query.Where(x => x.MinimumExperience >= minExperience && x.MinimumExperience <= maxExperience).ToList() | |||||
| : query.Where(x => x.MinimumExperience >= minExperience && x.MinimumExperience <= maxExperience).ToList(); | |||||
| public static List<Ad> FilterByWorkType(this List<Ad> query, string workHour) => | |||||
| workHour.ToLower() == "parttime" ? query.Where(x => x.WorkHour == WorkHours.PartTime).ToList() : query.Where(x => x.WorkHour == WorkHours.FullTime).ToList(); | |||||
| public static List<Ad> FilterByEmploymentType(this List<Ad> query, string employmentType) => | |||||
| employmentType.ToLower() == "intership" ? query.Where(x => x.EmploymentType == EmploymentTypes.Intership).ToList() : query.Where(x => x.EmploymentType == EmploymentTypes.Work).ToList(); | |||||
| public static List<Ad> FilterByTechnologies(this List<Ad> query, string[] technologies) | |||||
| { | |||||
| if (technologies == null || technologies.Length == 0) | |||||
| { | |||||
| return query; | |||||
| } | |||||
| List<Ad> filteredAds = new List<Ad>(); | |||||
| for (int i = 0; i < query.Count(); i++) | |||||
| { | |||||
| for (int j = 0; j < query[i].Technologies.Count(); j++) | |||||
| { | |||||
| var s = 0; | |||||
| for (int k = 0; k < technologies.Length; k++) | |||||
| { | |||||
| if (query[i].Technologies[j].Name.ToLower() == technologies[k].ToLower()) | |||||
| { | |||||
| s = 1; | |||||
| } | |||||
| } | |||||
| if (s == 1) | |||||
| { | |||||
| filteredAds.Add(query[i]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| return filteredAds; | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| using static Diligent.WebAPI.Data.Entities.Applicant; | |||||
| namespace Diligent.WebAPI.Business.Extensions | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public static class ApplicantExtensions | |||||
| { | |||||
| public static List<Applicant> FilterApplicants(this List<Applicant> query,ApplicantFilterDto applicantFilterDto) | |||||
| { | |||||
| return query.FilterByExperience(applicantFilterDto.MinExperience, applicantFilterDto.MaxExperience) | |||||
| .FilterByEmploymentType(applicantFilterDto.EmploymentType) | |||||
| .FilterByDateOfApplication(applicantFilterDto.MinDateOfApplication, applicantFilterDto.MaxDateOfApplication) | |||||
| .FilterByTechnologies(applicantFilterDto.Technologies).ToList(); | |||||
| } | |||||
| public static List<Ad> FilterAdApplicants(this List<Ad> query, ApplicantFilterDto applicantFilterDto) | |||||
| { | |||||
| List<Ad> filteredAds = new(); | |||||
| List<List<Applicant>> applicants = new(); | |||||
| for (int i = 0; i < query.Count; i++) | |||||
| { | |||||
| var app = query[i].Applicants.FilterApplicants(applicantFilterDto); | |||||
| applicants.Add(app); | |||||
| var k = query[i]; | |||||
| k.Applicants = applicants[i]; | |||||
| filteredAds.Add(k); | |||||
| } | |||||
| return filteredAds; | |||||
| } | |||||
| private static List<Applicant> FilterByExperience(this List<Applicant> query, int minExperience, int maxExperience) | |||||
| { | |||||
| if ((minExperience == 0 && maxExperience == 0) || minExperience > maxExperience) return query; | |||||
| return query.Where(x => x.Experience >= minExperience && x.Experience <= maxExperience).ToList(); | |||||
| } | |||||
| private static List<Applicant> FilterByEmploymentType(this List<Applicant> query, string? employmentType) | |||||
| { | |||||
| if (employmentType == null) return query; | |||||
| return query.Where(x => x.TypeOfEmployment == Enum.Parse<TypesOfEmployment>(employmentType)).ToList(); | |||||
| } | |||||
| private static List<Applicant> FilterByDateOfApplication(this List<Applicant> query, DateTime? minDateOfApplication, DateTime? maxDateOfApplication) | |||||
| { | |||||
| if (minDateOfApplication == null) return query; | |||||
| if (minDateOfApplication > maxDateOfApplication) return query; | |||||
| if (maxDateOfApplication == null) return query.Where(x => x.DateOfApplication >= minDateOfApplication && x.DateOfApplication <= DateTime.Now).ToList(); | |||||
| return query.Where(x => x.DateOfApplication >= minDateOfApplication && x.DateOfApplication < maxDateOfApplication).ToList(); | |||||
| } | |||||
| private static List<Applicant> FilterByTechnologies(this List<Applicant> query, string[]? technologies) | |||||
| { | |||||
| if (technologies is null) | |||||
| { | |||||
| return query; | |||||
| } | |||||
| List<Applicant> filteredApplicants = new(); | |||||
| for (int i = 0; i < query.Count; i++) | |||||
| { | |||||
| for (int j = 0; j < query[i].TechnologyApplicants.Count; j++) | |||||
| { | |||||
| bool s = false; | |||||
| for (int n = 0; n < technologies.Length; n++) | |||||
| { | |||||
| if (query[i].TechnologyApplicants[j].Technology.Name.ToLower() == technologies[n].ToLower()) | |||||
| { | |||||
| s = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (s) | |||||
| { | |||||
| filteredApplicants.Add(query[i]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| return filteredApplicants; | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.Extensions | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public static class PaginationExtension | |||||
| { | |||||
| public static List<T> ApplyPagging<T>(this List<T> query, Pagination pagination) | |||||
| { | |||||
| return query.Skip((pagination.CurrentPage - 1) * pagination.PageSize) | |||||
| .Take(pagination.PageSize).ToList(); | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Pattern; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Business.Extensions | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public static class PatternExtension | |||||
| { | |||||
| public static List<Pattern> FilterApplicants(this List<Pattern> query, FilterPatternDto filterPatternDto) | |||||
| { | |||||
| return query.FilterByDate(filterPatternDto.FromDate, filterPatternDto.ToDate) | |||||
| .FilterBySelectionLevels(filterPatternDto.SelectionLevels) | |||||
| .ToList(); | |||||
| } | |||||
| private static List<Pattern> FilterByDate(this List<Pattern> query, DateTime? fromDate, DateTime? toDate) | |||||
| { | |||||
| if(fromDate == null && toDate == null) return query; | |||||
| if(fromDate == null && toDate != null) return query.Where(x => x.CreatedAt <= toDate).ToList(); | |||||
| if ((fromDate != null && toDate == null) || (fromDate > toDate)) return query.Where(x => x.CreatedAt >= fromDate).ToList(); | |||||
| return query.Where(x => x.CreatedAt >= fromDate && x.CreatedAt < toDate).ToList(); | |||||
| } | |||||
| private static List<Pattern> FilterBySelectionLevels(this List<Pattern> query, int[]? selectionLevels) | |||||
| { | |||||
| if (selectionLevels is null) | |||||
| { | |||||
| return query; | |||||
| } | |||||
| List<Pattern> filteredPatterns = new(); | |||||
| for (int i = 0; i < query.Count; i++) | |||||
| { | |||||
| for(int j = 0; j < selectionLevels.Length; j++) | |||||
| { | |||||
| if (query[i].SelectionLevelId == selectionLevels[j]) | |||||
| { | |||||
| filteredPatterns.Add(query[i]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| return filteredPatterns; | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Extensions | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public static class SelectionProcessExtensions | |||||
| { | |||||
| public static List<SelectionLevel> FilterLevels(this List<SelectionLevel> query, SelectionProcessFilterDto filter) | |||||
| { | |||||
| var filteredLevels = new List<SelectionLevel>(); | |||||
| // If filters are empty | |||||
| if ((filter == null) || (!filter.DateStart.HasValue && !filter.DateEnd.HasValue && filter.Statuses != null && filter.Statuses.Length == 0)) | |||||
| return query; | |||||
| foreach (var level in query) | |||||
| { | |||||
| List<SelectionProcess> selectionProcesses = level.SelectionProcesses; | |||||
| if (filter.DateStart.HasValue) | |||||
| { | |||||
| selectionProcesses = level.SelectionProcesses.Where(sp => sp.Date >= filter.DateStart.Value).ToList(); | |||||
| } | |||||
| if (filter.DateEnd.HasValue) | |||||
| { | |||||
| selectionProcesses = selectionProcesses.Where(sp => sp.Date <= filter.DateEnd.Value).ToList(); | |||||
| } | |||||
| var filteredLevel = new SelectionLevel { Id = level.Id, Name = level.Name, SelectionProcesses = level.SelectionProcesses}; | |||||
| if(filter.Statuses != null && filter.Statuses.Length > 0) { | |||||
| filteredLevel.SelectionProcesses = selectionProcesses.Where(f => filter.Statuses.Contains(f.Status)).ToList(); | |||||
| } | |||||
| filteredLevels.Add(filteredLevel); | |||||
| } | |||||
| return filteredLevels; | |||||
| } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Business.Helper | |||||
| { | |||||
| public static class HTMLHelper | |||||
| { | |||||
| public static string RenderForgotPasswordPage(string url) | |||||
| { | |||||
| return "<div style=\"font-family: sans-serif\">" + | |||||
| "<div style=\"font-family: sans-serif;text-align: center;\">" + | |||||
| "<h2 style=\"color: #017397;\">HR Center Password Reset</h2>" + | |||||
| "<p style=\"font-size: 20px\">" + | |||||
| "To reset your HR Center password, please click on the button below." + | |||||
| "</p>" + | |||||
| "<a style = \"color: white;text-decoration:none;background-color: #017397;cursor: pointer;font-size: 20px;width: 220px;text-align: center;border-radius: 5px;padding: 5px 15px;height: 25px;\" " + | |||||
| $"href=\"{url}\">" + | |||||
| " RESET PASSWORD" + | |||||
| "</a>" + | |||||
| "<p style = \"font-size: 12px; margin-top: 25px;\" >" + | |||||
| "Please do not reply to this email.This message was sent from a notification-only address that is not monitored." + | |||||
| "</p>" + | |||||
| "</div>" + | |||||
| "</div>"; | |||||
| } | |||||
| public static string RenderRegisterPage(string url) | |||||
| { | |||||
| return "<div style=\"font-family: sans-serif\">" + | |||||
| "<div style=\"font-family: sans-serif;text-align: center;\">" + | |||||
| "<h2 style=\"color: #017397;\">Welcome to HR Center</h2>" + | |||||
| "<p style=\"font-size: 20px\">" + | |||||
| "To register, please click on the button below." + | |||||
| "</p>" + | |||||
| "<a style = \"color: white;text-decoration:none;background-color: #017397;cursor: pointer;font-size: 20px;width: 220px;text-align: center;border-radius: 5px;padding: 5px 15px;height: 25px;\" " + | |||||
| $"href=\"{url}\">" + | |||||
| " Click here to register" + | |||||
| "</a>" + | |||||
| "<p style = \"font-size: 12px; margin-top: 25px;\" >" + | |||||
| "Please do not reply to this email.This message was sent from a notification-only address that is not monitored." + | |||||
| "</p>" + | |||||
| "</div>" + | |||||
| "</div>"; | |||||
| } | |||||
| public static string RenderTagPage(string url) | |||||
| { | |||||
| return "<div>" + | |||||
| "<a style = \"color: white;text-decoration:none;background-color: #017397;cursor: pointer;font-size: 20px;border-radius: 5px;padding: 5px 15px;height: 25px;margin-top:10px;\" " + | |||||
| $"href=\"{url}\">" + | |||||
| "Click here to see the comment" + | |||||
| "</a>" + | |||||
| "</div>"; | |||||
| } | |||||
| public static string SuccessfulStep(string message, string pattern, string date) | |||||
| { | |||||
| return "<div style=\"font-family: sans-serif\">" + | |||||
| "<div style=\"font-family: sans-serif; text-align: center; \">" + | |||||
| "<h2 style=\"color: #017397;\">" + pattern + "</h2>" + | |||||
| "</div>" + | |||||
| "<div style=\"padding: 0.25rem 2rem 0 2rem\">" + | |||||
| "<p style=\"color: #017397;\">Poštovani,</p >" + | |||||
| "</div>" + | |||||
| "<div style=\"padding: 0 13rem 0 4rem; \">" + | |||||
| "<p style=\"color: #017397;\">" + message + " " + date + "</p>" + | |||||
| "</div>" + "<div style=\"padding: 0 2rem; \">" + | |||||
| "<p style=\"color: #017397;\">Srdačan pozdrav,<br>Diligent HR Team</p>" + | |||||
| "</div>" + | |||||
| "</div>"; | |||||
| } | |||||
| //public static string ScheduleInterview(string pattern, string message) | |||||
| //{ | |||||
| // Dictionary<string, string> Patterns = new Dictionary<string, string> | |||||
| // { | |||||
| // { "Neuspesan korak", $"<div><p>Neuspesan korak: {message}</p></div>" }, | |||||
| // { "Uspesan korak", SuccessfulStep(message, pattern) } | |||||
| // }; | |||||
| // return Patterns[pattern]; | |||||
| //} | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Helper | |||||
| { | |||||
| public static class StringGenerator | |||||
| { | |||||
| public static string GenerateRandomPassword(PasswordOptions opts = null) | |||||
| { | |||||
| if (opts == null) opts = new PasswordOptions() | |||||
| { | |||||
| RequiredLength = 8, | |||||
| RequiredUniqueChars = 4, | |||||
| RequireDigit = true, | |||||
| RequireLowercase = true, | |||||
| RequireNonAlphanumeric = true, | |||||
| RequireUppercase = true | |||||
| }; | |||||
| if(opts.RequiredLength < 4) | |||||
| opts.RequiredLength = 4; | |||||
| string[] randomChars = new[] { | |||||
| "ABCDEFGHJKLMNOPQRSTUVWXYZ", // uppercase | |||||
| "abcdefghijkmnopqrstuvwxyz", // lowercase | |||||
| "0123456789", // digits | |||||
| "!@$?_-" // non-alphanumeric | |||||
| }; | |||||
| Random rand = new(Environment.TickCount); | |||||
| List<char> chars = new List<char>(); | |||||
| if (opts.RequireUppercase) | |||||
| chars.Insert(rand.Next(0, chars.Count), | |||||
| randomChars[0][rand.Next(0, randomChars[0].Length)]); | |||||
| if (opts.RequireLowercase) | |||||
| chars.Insert(rand.Next(0, chars.Count), | |||||
| randomChars[1][rand.Next(0, randomChars[1].Length)]); | |||||
| if (opts.RequireDigit) | |||||
| chars.Insert(rand.Next(0, chars.Count), | |||||
| randomChars[2][rand.Next(0, randomChars[2].Length)]); | |||||
| if (opts.RequireNonAlphanumeric) | |||||
| chars.Insert(rand.Next(0, chars.Count), | |||||
| randomChars[3][rand.Next(0, randomChars[3].Length)]); | |||||
| for (int i = chars.Count; i < opts.RequiredLength | |||||
| || chars.Distinct().Count() < opts.RequiredUniqueChars; i++) | |||||
| { | |||||
| string rcs = randomChars[rand.Next(0, randomChars.Length)]; | |||||
| chars.Insert(rand.Next(0, chars.Count), | |||||
| rcs[rand.Next(0, rcs.Length)]); | |||||
| } | |||||
| return new string(chars.ToArray()); | |||||
| } | |||||
| } | |||||
| } |
| | |||||
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| public class AdMappingProfile : Profile | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public AdMappingProfile() | |||||
| { | |||||
| #region DTO to Model | |||||
| CreateMap<AdCreateDto, Ad>(); | |||||
| CreateMap<AdUpdateDto, Ad>(); | |||||
| #endregion | |||||
| #region Model to DTO | |||||
| CreateMap<Ad, AdResponseDto>(); | |||||
| CreateMap<Ad, AdDetailsResponseDto>(); | |||||
| CreateMap<Ad, AdApplicantsViewDto>(); | |||||
| CreateMap<Ad, AdResponseWithCountDto>() | |||||
| .ForMember(dest => dest.Count, opt => opt.MapFrom(x => x.Applicants.Count)); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class ApplicantMappingProfile:Profile | |||||
| { | |||||
| public ApplicantMappingProfile() | |||||
| { | |||||
| #region Models to DTOs | |||||
| CreateMap<Applicant, ApplicantViewDto>(); | |||||
| CreateMap<Applicant, AdApplicantViewDto>(); | |||||
| CreateMap<Applicant, ApplicantScheduleViewDto>(); | |||||
| CreateMap<Applicant, PatternApplicantViewDto>(); | |||||
| CreateMap<Applicant, ApplicantOptionsDTO>(); | |||||
| #endregion | |||||
| #region DTOs to Models | |||||
| CreateMap<ApplicantImportDto, Applicant>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Comment; | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| public class CommentMappingProfile:Profile | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public CommentMappingProfile() | |||||
| { | |||||
| #region Models to DTO | |||||
| CreateMap<Comment, CommentViewDto>(); | |||||
| #endregion | |||||
| #region DTO to Model | |||||
| CreateMap<CommentCreateDto, Comment>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| public class CompanyMappingProfile : Profile | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public CompanyMappingProfile() | |||||
| { | |||||
| #region Models to DTOs | |||||
| CreateMap<InsuranceCompany, InsuranceCompanyViewDto>(); | |||||
| #endregion | |||||
| #region DTOs to Models | |||||
| CreateMap<InsuranceCompanyCreateDto, InsuranceCompany>(); | |||||
| CreateMap<InsuranceCompanyUpdateDto, InsuranceCompany>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| public class InsurerMappingProfile : Profile | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public InsurerMappingProfile() | |||||
| { | |||||
| #region DTO to Model | |||||
| CreateMap<InsurerCreateDto, Insurer>(); | |||||
| CreateMap<InsurerUpdateDto, Insurer>(); | |||||
| #endregion | |||||
| #region Model to DTO | |||||
| CreateMap<Insurer, InsurerViewDto>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Pattern; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class PatternMappingProfile : Profile | |||||
| { | |||||
| public PatternMappingProfile() | |||||
| { | |||||
| #region DTO to Model | |||||
| CreateMap<PatternCreateDto, Pattern>(); | |||||
| CreateMap<PatternUpdateDto, Pattern>(); | |||||
| #endregion | |||||
| #region Model to DTO | |||||
| CreateMap<Pattern, PatternResponseDto>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| public class PolicyMappingProfiles : Profile | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public PolicyMappingProfiles() | |||||
| { | |||||
| CreateMap<InsurancePolicy, InsurancePolicyViewDto>(); | |||||
| CreateMap<InsurancePolicyCreateDto, InsurancePolicy>(); | |||||
| CreateMap<InsurancePolicyUpdateDto, InsurancePolicy>(); | |||||
| } | |||||
| } | |||||
| } |
| | |||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionLevel; | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class SelectionLevelMappingProfile : Profile | |||||
| { | |||||
| public SelectionLevelMappingProfile() | |||||
| { | |||||
| #region Model to DTO | |||||
| CreateMap<SelectionLevel, SelectionLevelResposneDto>(); | |||||
| CreateMap<SelectionLevel, SelectionLevelResponseWithDataDto>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Schedule; | |||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| public class SelectionProcessMappingProfile : Profile | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public SelectionProcessMappingProfile() | |||||
| { | |||||
| #region DTO to Model | |||||
| CreateMap<SelectionProcessCreateDto, SelectionProcess>(); | |||||
| CreateMap<SelectionProcessUpdateStatusDto, SelectionProcess>(); | |||||
| #endregion | |||||
| #region Model to DTO | |||||
| CreateMap<SelectionProcess, SelectionProcessResposneDto>(); | |||||
| CreateMap<SelectionProcess, SelectionProcessResposneWithoutApplicantDto>(); | |||||
| CreateMap<SelectionProcess, ScheduleViewDto>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| public class TechnologyMappingProfile : Profile | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public TechnologyMappingProfile() | |||||
| { | |||||
| #region Model to DTO | |||||
| CreateMap<Technology, TechnologyResponseDto>(); | |||||
| //CreateMap<TechnologyApplicant, TechnologyResponseDto>(); -- ermin | |||||
| CreateMap<TechnologyApplicant, TechnologyViewDto>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.User; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class UserMappingProfile : Profile | |||||
| { | |||||
| public UserMappingProfile() | |||||
| { | |||||
| #region DTO to Model | |||||
| CreateMap<CreateUserRequestDto, User>(); | |||||
| CreateMap<RegisterDTO, User>().ForMember(n => n.PhoneNumber, opt => opt.MapFrom(n => n.Phone)); | |||||
| #endregion | |||||
| #region Model to DTO | |||||
| CreateMap<User, UserResponseDTO>(); | |||||
| CreateMap<User, UserDetailsResponseDTO>() | |||||
| .ForMember(dest => dest.PhoneNumber, opt => opt.NullSubstitute("User has no phone number saved.")) | |||||
| .ForMember(dest => dest.Position, opt => opt.NullSubstitute("Position has not been declared yet.")) | |||||
| .ForMember(dest => dest.LinkedIn, opt => opt.NullSubstitute("User takes no part in any social media.")); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.MappingProfiles | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class WebhookMappingProfile : Profile | |||||
| { | |||||
| public WebhookMappingProfile() | |||||
| { | |||||
| #region DTO to Model | |||||
| CreateMap<WebhookSubscriptionCreateDto, WebhookSubscription>(); | |||||
| #endregion | |||||
| #region Model to DTO | |||||
| CreateMap<WebhookDefinition, WebhookDefinitionViewDto>(); | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class AdService : IAdService | |||||
| { | |||||
| private readonly ILogger<AdService> _logger; | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly ITechnologyService _technologyService; | |||||
| public AdService(DatabaseContext context, IMapper mapper, ITechnologyService technologyService, ILogger<AdService> logger) | |||||
| { | |||||
| _logger = logger; | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| _technologyService = technologyService; | |||||
| } | |||||
| public async Task<List<AdResponseDto>> GetAllAsync() | |||||
| { | |||||
| _logger.LogInformation("Start getting all Ads"); | |||||
| var today = DateTime.Now; | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var fromDb = await _context.Ads.Include(x => x.Technologies).Where(x => x.ExpiredAt > today).ToListAsync(); | |||||
| _logger.LogInformation($"Received {fromDb.Count} ads from db."); | |||||
| _logger.LogInformation($"Mapping received ads to AdResponseDto"); | |||||
| var result = _mapper.Map<List<AdResponseDto>>(fromDb); | |||||
| _logger.LogInformation($"Ads has been mapped and received to client: {result.Count} mapped ads"); | |||||
| return result; | |||||
| } | |||||
| public async Task<List<AdResponseWithCountDto>> GetAllWithCountAsync() | |||||
| { | |||||
| _logger.LogInformation("Start getting all Ads with applicants count"); | |||||
| var today = DateTime.Now; | |||||
| _logger.LogInformation("Getting data from database"); | |||||
| var res = _mapper.Map<List<AdResponseWithCountDto>>(await _context.Ads.Include(x => x.Applicants).Where(x => x.ExpiredAt > today).ToListAsync()); | |||||
| _logger.LogInformation($"Received {res.Count} ads with their counts"); | |||||
| return res; | |||||
| } | |||||
| public async Task<AdResponseDto> GetByIdAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||||
| var ad = await _context.Ads.FindAsync(id); | |||||
| if (ad is null) | |||||
| { | |||||
| _logger.LogError($"Ad with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Ad not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Ad with id = {id}"); | |||||
| AdResponseDto result = _mapper.Map<AdResponseDto>(ad); | |||||
| _logger.LogInformation($"Ad with id = {id} mapped successfully"); | |||||
| return result; | |||||
| } | |||||
| public async Task<AdDetailsResponseDto> GetAdDetailsByIdAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start finding Ad with id = {id} with applicants"); | |||||
| var ad = await _context.Ads.Include(x => x.Applicants).Where(x => x.Id == id).FirstOrDefaultAsync(); | |||||
| if (ad is null) | |||||
| { | |||||
| _logger.LogError($"Ad with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Ad not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Ad with id = {id}"); | |||||
| AdDetailsResponseDto result = _mapper.Map<AdDetailsResponseDto>(ad); | |||||
| _logger.LogInformation($"Ad with id = {id} mapped successfully"); | |||||
| return result; | |||||
| } | |||||
| public async Task<List<AdResponseDto>> GetArchiveAds() | |||||
| { | |||||
| _logger.LogInformation("Start getting all Archived Ads"); | |||||
| var today = DateTime.Now; | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var archiveAds = await _context.Ads.Where(x => x.ExpiredAt < today).ToListAsync(); | |||||
| _logger.LogInformation($"Received {archiveAds.Count} ads from db."); | |||||
| _logger.LogInformation($"Mapping received ads to AdResponseDto"); | |||||
| List<AdResponseDto> result = _mapper.Map<List<AdResponseDto>>(archiveAds); | |||||
| _logger.LogInformation($"Ads has been mapped and received to client: {result.Count} mapped ads"); | |||||
| return result; | |||||
| } | |||||
| public async Task<List<AdResponseDto>> GetFilteredAdsAsync(AdFilterDto filters) | |||||
| { | |||||
| _logger.LogInformation($"Start getting all filtered Ads"); | |||||
| var today = DateTime.Now; | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var filteredAds = await _context.Ads.Include(x => x.Technologies).Where(x => x.ExpiredAt > today).ToListAsync(); | |||||
| _logger.LogInformation($"Received {filteredAds.Count} ads from db."); | |||||
| _logger.LogInformation($"Mapping received ads to AdResponseDto"); | |||||
| List<AdResponseDto> result = _mapper.Map<List<AdResponseDto>>(filteredAds.Filter(filters)); | |||||
| _logger.LogInformation($"Ads has been mapped and received to client: {result.Count} mapped ads"); | |||||
| return result; | |||||
| } | |||||
| public async Task CreateAsync(AdCreateDto adCreateDto) | |||||
| { | |||||
| _logger.LogInformation($"Start creating Ad"); | |||||
| var ad = _mapper.Map<Ad>(adCreateDto); | |||||
| _logger.LogInformation($"Ad created successfully"); | |||||
| _logger.LogInformation($"Start adding technologies to Ad"); | |||||
| for (int i = 0; i < adCreateDto.TechnologiesIds.Count; i++) | |||||
| { | |||||
| var technology = await _technologyService.GetEntityByIdAsync(adCreateDto.TechnologiesIds[i]); | |||||
| ad.Technologies.Add(technology); | |||||
| _logger.LogInformation($"Technology with id {technology.TechnologyId} added to Ad"); | |||||
| } | |||||
| _logger.LogInformation($"Finished adding techonologies"); | |||||
| await _context.Ads.AddAsync(ad); | |||||
| _logger.LogInformation($"Saving Ad to db..."); | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Ad saved to DB"); | |||||
| await result; | |||||
| } | |||||
| public async Task UpdateAsync(int id, AdUpdateDto adUpdateDto) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||||
| var ad = await _context.Ads.FindAsync(id); | |||||
| if (ad is null) | |||||
| { | |||||
| _logger.LogError($"Ad with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Ad not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Ad with id = {id}"); | |||||
| _mapper.Map(adUpdateDto, ad); | |||||
| _logger.LogInformation($"Ad with id = {id} mapped successfully"); | |||||
| _context.Entry(ad).State = EntityState.Modified; | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Ad saved to DB"); | |||||
| await result; | |||||
| } | |||||
| public async Task ArchiveAdAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||||
| var ad = await _context.Ads.FindAsync(id); | |||||
| if (ad is null) | |||||
| { | |||||
| _logger.LogError($"Ad with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Ad not found"); | |||||
| } | |||||
| _logger.LogInformation($"Change ad expired time"); | |||||
| ad.ExpiredAt = DateTime.Now; | |||||
| _logger.LogInformation($"Ad expired time changed successfully"); | |||||
| _context.Entry(ad).State = EntityState.Modified; | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Ad saved to DB"); | |||||
| await result; | |||||
| } | |||||
| public async Task DeleteAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||||
| var ad = await _context.Ads.FindAsync(id); | |||||
| if (ad is null) | |||||
| { | |||||
| _logger.LogError($"Ad with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Ad not found"); | |||||
| } | |||||
| _context.Ads.Remove(ad); | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Ad saved to DB"); | |||||
| await result; | |||||
| } | |||||
| public async Task<Ad> ImportAsync(AdCreateDto adCreateDto) | |||||
| { | |||||
| _logger.LogInformation($"Start importing Ad"); | |||||
| var ad = _mapper.Map<Ad>(adCreateDto); | |||||
| _logger.LogInformation($"Ad imported successfully"); | |||||
| await _context.Ads.AddAsync(ad); | |||||
| _logger.LogInformation($"Saving Ad to db..."); | |||||
| await _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Ad saved to DB"); | |||||
| return ad; | |||||
| } | |||||
| public async Task<Ad> GetByIdEntityAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||||
| var ad = await _context.Ads.FindAsync(id); | |||||
| if (ad is null) | |||||
| { | |||||
| _logger.LogError($"Ad with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Ad not found"); | |||||
| } | |||||
| return ad; | |||||
| } | |||||
| } | |||||
| } |
| using static Diligent.WebAPI.Data.Entities.Applicant; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class ApplicantService : IApplicantService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly ILogger<ApplicantService> _logger; | |||||
| private readonly IUserService _userService; | |||||
| private readonly IFileService _fileService; | |||||
| private readonly IAdService _adService; | |||||
| private readonly ITechnologyService _technologyService; | |||||
| public ApplicantService(DatabaseContext context, IMapper mapper, ILogger<ApplicantService> logger, | |||||
| IUserService userService,IFileService fileService,IAdService adService,ITechnologyService technologyService) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| _logger = logger; | |||||
| _userService = userService; | |||||
| _fileService = fileService; | |||||
| _adService = adService; | |||||
| _technologyService = technologyService; | |||||
| } | |||||
| public async Task<QueryResultDto<ApplicantViewDto>> GetFilteredApplicants(ApplicantFilterDto applicantFilterDto) | |||||
| { | |||||
| _logger.LogInformation("Start getting filtered applicants"); | |||||
| _logger.LogInformation("Getting data from DB and filter"); | |||||
| var filteredApplicants = (await _context.Applicants | |||||
| .Include(c => c.Ads) | |||||
| .Include(x => x.TechnologyApplicants) | |||||
| .ThenInclude(x => x.Technology).ToListAsync()) | |||||
| .FilterApplicants(applicantFilterDto); | |||||
| int totalNumberOfItems = filteredApplicants.Count; | |||||
| _logger.LogInformation($"Got {totalNumberOfItems} applicants"); | |||||
| filteredApplicants = PaginationExtension.ApplyPagging(filteredApplicants, new Pagination | |||||
| { | |||||
| CurrentPage = applicantFilterDto.CurrentPage, | |||||
| PageSize = applicantFilterDto.PageSize | |||||
| }); | |||||
| _logger.LogInformation($"Return list of applicants"); | |||||
| return new QueryResultDto<ApplicantViewDto> | |||||
| { | |||||
| Items = _mapper.Map<List<ApplicantViewDto>>(filteredApplicants), | |||||
| Total = totalNumberOfItems | |||||
| }; | |||||
| } | |||||
| public async Task<ApplicantViewDto> GetById(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Applicant with id = {id}"); | |||||
| var applicant = await _context.Applicants | |||||
| .Include(x => x.Ads) | |||||
| .ThenInclude(x => x.Technologies) | |||||
| .Include(x => x.TechnologyApplicants) | |||||
| .ThenInclude(x => x.Technology) | |||||
| .Include(x => x.Comments) | |||||
| .ThenInclude(t => t.User) | |||||
| .FirstOrDefaultAsync(x => x.ApplicantId == id); | |||||
| if (applicant is null) | |||||
| { | |||||
| _logger.LogError($"Applicant with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Applicant not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Applicant with id = {id}"); | |||||
| var result = _mapper.Map<ApplicantViewDto>(applicant); | |||||
| result.CV = await _fileService.GetCV("638077305621281656.pdf"); | |||||
| _logger.LogInformation($"Applicant with id = {id} mapped successfully"); | |||||
| return result; | |||||
| } | |||||
| public async Task<ApplicantViewDto> GetApplicantWithSelectionProcessesById(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Applicant with id = {id}"); | |||||
| var applicant = await _context.Applicants | |||||
| .Include(a => a.SelectionProcesses).ThenInclude(sp => sp.SelectionLevel) | |||||
| .Include(a => a.SelectionProcesses).ThenInclude(sp => sp.Scheduler) | |||||
| .FirstOrDefaultAsync(a => a.ApplicantId == id); | |||||
| if (applicant is null) | |||||
| { | |||||
| _logger.LogError($"Applicant with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Applicant not found"); | |||||
| } | |||||
| _logger.LogInformation($"Applicant with id = {id} mapped successfully"); | |||||
| return _mapper.Map<ApplicantViewDto>(applicant); | |||||
| } | |||||
| public async Task DeleteApplicant(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Applicant with id = {id}"); | |||||
| var applicant = await _context.Applicants.FindAsync(id); | |||||
| if (applicant is null) | |||||
| { | |||||
| _logger.LogError($"Applicant with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Applicant not found"); | |||||
| } | |||||
| _logger.LogInformation($"Removing Applicant with id = {id}"); | |||||
| _context.Applicants.Remove(applicant); | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Applicant with id = {id} is removed successfully"); | |||||
| await result; | |||||
| } | |||||
| public async Task<List<AdApplicantsViewDto>> GetAllAdsApplicants(ApplicantFilterDto applicantFilterDto) | |||||
| { | |||||
| _logger.LogInformation("Start getting filtered applicants"); | |||||
| _logger.LogInformation("Getting data from DB and filter"); | |||||
| var adsApplicants = (await _context.Ads | |||||
| .Include(a => a.Applicants) | |||||
| .ThenInclude(a => a.TechnologyApplicants) | |||||
| .ThenInclude(a => a.Technology) | |||||
| .ToListAsync()) | |||||
| .FilterAdApplicants(applicantFilterDto); | |||||
| _logger.LogInformation($"Got {adsApplicants.Count} ads"); | |||||
| _logger.LogInformation("Mapping received Ads to AdApplicantsViewDto"); | |||||
| var result = _mapper.Map<List<AdApplicantsViewDto>>(adsApplicants); | |||||
| _logger.LogInformation($"Ads mapped successfully"); | |||||
| return result; | |||||
| } | |||||
| public async Task ApplyForAd(ApplyForAdRequestDto request) | |||||
| { | |||||
| string fileName = string.Format(@"{0}.pdf", DateTime.Now.Ticks); | |||||
| _logger.LogInformation($"Start uploading CV of applicant on Azure Blob storage"); | |||||
| await _fileService.UploadCV(fileName, request.PdfFile); | |||||
| _logger.LogInformation($"CV uploaded on Azure Blob storage"); | |||||
| _logger.LogInformation("Start applying for ad"); | |||||
| _logger.LogInformation("Find ad by id"); | |||||
| var ad = await _adService.GetByIdEntityAsync(request.AdId); | |||||
| if (ad == null) | |||||
| { | |||||
| _logger.LogError($"Ad with {request.AdId} not found"); | |||||
| throw new EntityNotFoundException("Ad not found in database"); | |||||
| } | |||||
| _logger.LogInformation($"Find sent technologies from FE in database"); | |||||
| //var technologies = await _context.Technologies.Where(x => request.TechnologiesIds.Contains(x.TechnologyId)).ToListAsync(); | |||||
| var technologies = await _technologyService.GetEntitiesAsync(request.TechnologiesIds); | |||||
| _logger.LogInformation($"Create applicant instance with sent data"); | |||||
| Applicant applicant = new() | |||||
| { | |||||
| FirstName = request.FirstName, | |||||
| LastName = request.LastName, | |||||
| Position = ad.Title, | |||||
| DateOfApplication = DateTime.Now, | |||||
| CV = fileName, | |||||
| Email = request.Email, | |||||
| PhoneNumber = request.PhoneNumber, | |||||
| GithubLink = request.GithubLink, | |||||
| LinkedlnLink = request.LinkedinLink, | |||||
| BitBucketLink = request.BitBucketLink, | |||||
| Experience = request.Experience, | |||||
| //TypeOfEmployment = (EmploymentTypes)Enum.Parse(typeof(EmploymentTypes), ad.EmploymentType, true), | |||||
| TypeOfEmployment = ad.EmploymentType == EmploymentTypes.Intership ? TypesOfEmployment.Intership : TypesOfEmployment.Posao, | |||||
| Comments = new(), | |||||
| Ads = new List<Ad> { ad }, | |||||
| SelectionProcesses = new(), | |||||
| TechnologyApplicants = new(), | |||||
| ApplicationChannel = "Putem sajta", | |||||
| Gender = request.Gender == "Muski" ? Genders.M : Genders.Z, | |||||
| ProfessionalQualification = request.ProfessionalQualification | |||||
| }; | |||||
| _logger.LogInformation($"Saving applicant in database"); | |||||
| await _context.Applicants.AddAsync(applicant); | |||||
| var res = await _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Applicant saved in database"); | |||||
| _logger.LogInformation($"Saving TechnologyApplicants in database"); | |||||
| for (int i = 0; i < technologies.Count; i++) | |||||
| { | |||||
| await _context.ApplicantTechnologies.AddAsync(new TechnologyApplicant { Applicant = applicant, ApplicantId = applicant.ApplicantId, Technology = technologies[i], TechnologyId = technologies[i].TechnologyId }); | |||||
| } | |||||
| await _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"TechnologyApplicants saved in database"); | |||||
| } | |||||
| public async Task ImportApplicant(List<ApplicantImportDto> requests) | |||||
| { | |||||
| _logger.LogInformation($"Create applicant instance with sent data"); | |||||
| var res = new List<Applicant>(); | |||||
| _logger.LogInformation($"Get first user from database"); | |||||
| var user = await _userService.GetFirst(); | |||||
| _logger.LogInformation($"User succesufully fetched from database"); | |||||
| foreach (var request in requests) { | |||||
| Applicant applicant = new Applicant | |||||
| { | |||||
| FirstName = request.FirstName ?? "", | |||||
| LastName = request.LastName ?? "", | |||||
| CV = request.CV ?? "", | |||||
| Email = request.Email ?? "", | |||||
| PhoneNumber = request.PhoneNumber ?? "", | |||||
| GithubLink = request.GithubLink ?? "", | |||||
| LinkedlnLink = request.LinkedlnLink ?? "", | |||||
| BitBucketLink = request.BitBucketLink ?? "", | |||||
| Position = "", | |||||
| DateOfApplication = request.DateOfApplication, | |||||
| TypeOfEmployment = request.TypeOfEmployment == "Praksa" ? TypesOfEmployment.Intership : TypesOfEmployment.Posao, | |||||
| Experience = request.Experience, | |||||
| ApplicationChannel = request.ApplicationChannel ?? "Putem sajta", | |||||
| // MORA DA SE UVEDE KO JE DAO KOMENTAR DA LI DA STAVIMO DA JE DANIJELA SVIMA STAVILA KOMENTARE ILI ?? | |||||
| Comments = new List<Comment> | |||||
| { | |||||
| new Comment | |||||
| { | |||||
| User = user, | |||||
| Content = request.Comment | |||||
| } | |||||
| }, | |||||
| Ads = new List<Ad> { request.Ad }, | |||||
| SelectionProcesses = new(), | |||||
| TechnologyApplicants = new(), | |||||
| Gender = Genders.M, | |||||
| ProfessionalQualification = "Elektrotehnicki fakultet", | |||||
| }; | |||||
| res.Add(applicant); | |||||
| } | |||||
| _logger.LogInformation($"Saving applicants in database"); | |||||
| await _context.AddRangeAsync(res); | |||||
| await _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Applicants saved in database"); | |||||
| } | |||||
| public async Task<List<ApplicantOptionsDTO>> GetOptions() | |||||
| { | |||||
| _logger.LogInformation($"Start getting all applicants from database"); | |||||
| var res = await _context.Applicants.ToListAsync(); | |||||
| _logger.LogInformation($"Got {res.Count} applicants"); | |||||
| return _mapper.Map<List<ApplicantOptionsDTO>>(res); | |||||
| } | |||||
| public async Task<ServiceResponseDTO<object>> InitializeProcess(ApplicantProcessRequestDTO model) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Applicant with id = {model.ApplicantId}"); | |||||
| var applicant = await _context.Applicants.Include(n => n.SelectionProcesses).Where(n=> n.ApplicantId ==model.ApplicantId).FirstOrDefaultAsync(); | |||||
| if (applicant == null) | |||||
| { | |||||
| _logger.LogError($"Applicant with id = {model.ApplicantId} not found"); | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "Applicant does not exist." | |||||
| }; | |||||
| } | |||||
| applicant.SelectionProcesses.Add(new SelectionProcess | |||||
| { | |||||
| Name = StringGenerator.GenerateRandomPassword(), | |||||
| SchedulerId = model.SchedulerId, | |||||
| SelectionLevelId = 1, | |||||
| Status = model.Appointment != null ? "Zakazan" : "Čeka na zakazivanje", | |||||
| Date = model.Appointment | |||||
| }); | |||||
| _logger.LogInformation($"Saving selection processes in database"); | |||||
| await _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Selecetion processes saved in database"); | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| Data = true | |||||
| }; | |||||
| } | |||||
| } | |||||
| } |
| using Microsoft.AspNetCore.WebUtilities; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class AuthenticationService : IAuthenticationService | |||||
| { | |||||
| private readonly AuthorizationSettings _authSettings; | |||||
| private readonly FrontEndSettings _frontEndSettings; | |||||
| private readonly UserManager<User> _userManager; | |||||
| private readonly DatabaseContext _databaseContext; | |||||
| private readonly IEmailer _emailer; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly ILogger<AuthenticationService> _logger; | |||||
| private readonly IHttpClientService _httpClient; | |||||
| public AuthenticationService(IOptions<AuthorizationSettings> authSettings, | |||||
| IOptions<FrontEndSettings> frontEndSettings, | |||||
| UserManager<User> userManager, | |||||
| DatabaseContext databaseContext, | |||||
| IEmailer emailer, | |||||
| ILogger<AuthenticationService> logger, | |||||
| IHttpClientService httpClient, | |||||
| IMapper mapper) | |||||
| { | |||||
| _authSettings = authSettings.Value; | |||||
| _frontEndSettings = frontEndSettings.Value; | |||||
| _userManager = userManager; | |||||
| _databaseContext = databaseContext; | |||||
| _httpClient = httpClient; | |||||
| _emailer = emailer; | |||||
| _logger = logger; | |||||
| _mapper = mapper; | |||||
| } | |||||
| public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(AuthenticateRequestDto model) | |||||
| { | |||||
| _logger.LogInformation($"Checking credentials for user: {model.Username}"); | |||||
| var user = await _userManager.FindByNameAsync(model.Username); | |||||
| // return null if user not found | |||||
| if (user == null) | |||||
| { | |||||
| _logger.LogError($"User with username = {model.Username} not found"); | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "Username is not valid" | |||||
| }; | |||||
| } | |||||
| var result = await _userManager.CheckPasswordAsync(user, model.Password); | |||||
| // return null if user is disabled | |||||
| if (user.IsEnabled == false) | |||||
| { | |||||
| _logger.LogError($"User: {model.Username} is not enabled"); | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = $"User with email {model.Username} has no permission to log in." | |||||
| }; | |||||
| } | |||||
| // password is not correct | |||||
| if (!result) | |||||
| { | |||||
| _logger.LogError($"Password for user: {model.Username} is not correct"); | |||||
| await _userManager.AccessFailedAsync(user); | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "Password is not correct" | |||||
| }; | |||||
| } | |||||
| var token = await GenerateToken(user); | |||||
| _logger.LogInformation($"Successfull login token: {token}"); | |||||
| return token; | |||||
| } | |||||
| public async Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(GoogleApiModel model) | |||||
| { | |||||
| _logger.LogInformation($"Checking token for google login {model.Token}"); | |||||
| if (!(await _httpClient.IsTokenValid(model.Token))) | |||||
| { | |||||
| _logger.LogError($"Token is not valid"); | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "Invalid Google Api Token" | |||||
| }; | |||||
| } | |||||
| _logger.LogInformation($"Checking if user exists in Db with email : {model.User.email}"); | |||||
| var user = await _userManager.FindByEmailAsync(model.User.email); | |||||
| // return null if user not found | |||||
| if (user == null) | |||||
| { | |||||
| _logger.LogError($"User does not exist in Db"); | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = $"User with email {model.User.email} does not exist in database" | |||||
| }; | |||||
| } | |||||
| if (user.IsEnabled == false) | |||||
| { | |||||
| _logger.LogError($"User is not enabled"); | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = $"User with email {model.User.email} has no permission to log in." | |||||
| }; | |||||
| } | |||||
| var token = await GenerateToken(user); | |||||
| _logger.LogInformation($"Successfull login. Token :{token}"); | |||||
| return token; | |||||
| } | |||||
| private async Task<ServiceResponseDTO<AuthenticateResponseDto>> GenerateToken(User user) | |||||
| { | |||||
| var isLocked = await _userManager.IsLockedOutAsync(user); | |||||
| if (isLocked) | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "The account is locked out" | |||||
| }; | |||||
| // authentication successful so generate jwt token | |||||
| var token = await GenerateJwtToken(user, true); | |||||
| var data = new AuthenticateResponseDto | |||||
| { | |||||
| Id = user.Id, | |||||
| Username = user.UserName, | |||||
| FirstName = user.FirstName, | |||||
| LastName = user.LastName, | |||||
| Token = token, | |||||
| RefreshToken = token | |||||
| }; | |||||
| return new ServiceResponseDTO<AuthenticateResponseDto> | |||||
| { | |||||
| Data = data | |||||
| }; | |||||
| } | |||||
| private async Task<string> GenerateJwtToken(User user, bool authenticate = false) | |||||
| { | |||||
| // generate token that is valid for 7 days | |||||
| var tokenHandler = new JwtSecurityTokenHandler(); | |||||
| var key = Encoding.ASCII.GetBytes(_authSettings.Secret); | |||||
| var tokenDescriptor = new SecurityTokenDescriptor | |||||
| { | |||||
| Subject = new ClaimsIdentity(new[] { | |||||
| new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()), | |||||
| new Claim("id", user.Id.ToString()) | |||||
| }), | |||||
| Expires = DateTime.Now.AddMinutes(_authSettings.JwtExpiredTime), | |||||
| SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) | |||||
| }; | |||||
| var token = tokenHandler.CreateToken(tokenDescriptor); | |||||
| var writedToken = tokenHandler.WriteToken(token); | |||||
| var refreshToken = new RefreshToken | |||||
| { | |||||
| Token = writedToken, | |||||
| JwtId = user.Id.ToString(), | |||||
| UserId = user.Id, | |||||
| User = user, | |||||
| CreationDate = DateTime.Now, | |||||
| ExpiryDate = DateTime.Now.AddMinutes(_authSettings.JwtRefreshExpiredTime) | |||||
| }; | |||||
| var existRefreshToken = await _databaseContext.RefreshTokens.Where(x => x.UserId == user.Id).FirstOrDefaultAsync(); | |||||
| if (existRefreshToken != null) | |||||
| { | |||||
| existRefreshToken.Token = writedToken; | |||||
| existRefreshToken.JwtId = token.Id; | |||||
| existRefreshToken.CreationDate = DateTime.Now; | |||||
| existRefreshToken.ExpiryDate = DateTime.Now.AddMinutes(_authSettings.JwtRefreshExpiredTime); | |||||
| if (authenticate) | |||||
| { | |||||
| existRefreshToken.Used = false; | |||||
| existRefreshToken.Invalidated = false; | |||||
| } | |||||
| _databaseContext.RefreshTokens.Update(existRefreshToken); | |||||
| await UpdateRefreshToken(existRefreshToken); | |||||
| } | |||||
| else | |||||
| { | |||||
| await _databaseContext.RefreshTokens.AddAsync(refreshToken); | |||||
| } | |||||
| await _databaseContext.SaveChangesAsync(); | |||||
| _logger.LogInformation($"JWTToken : {writedToken}"); | |||||
| return writedToken; | |||||
| } | |||||
| public async Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model) | |||||
| { | |||||
| var validatedToken = GetPrincipalFromToken(model.Token, false); | |||||
| if (validatedToken == null) | |||||
| { | |||||
| return new RefreshTokenResultDto { Error = "Invalid token" }; | |||||
| } | |||||
| var jti = validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Jti).Value; | |||||
| var storedRefreshToken = await _databaseContext.RefreshTokens.SingleOrDefaultAsync(x => x.JwtId == jti); | |||||
| if (storedRefreshToken == null) | |||||
| { | |||||
| return new RefreshTokenResultDto { Error = "This refresh token does not exist" }; | |||||
| } | |||||
| var userk = await _databaseContext.Users.Where(u => u.Id == storedRefreshToken.UserId).FirstOrDefaultAsync(); | |||||
| if (userk == null) | |||||
| { | |||||
| return new RefreshTokenResultDto { Error = "There is no user which is associated with refresh token" }; | |||||
| } | |||||
| var expiryDateUnix = long.Parse(validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Exp).Value); | |||||
| var expiryDateTimeUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) | |||||
| .AddSeconds(expiryDateUnix); | |||||
| if (expiryDateTimeUtc > DateTime.UtcNow) | |||||
| { | |||||
| return new RefreshTokenResultDto | |||||
| { | |||||
| Data = new AuthenticateResponseDto | |||||
| { | |||||
| Id = userk.Id, | |||||
| FirstName = userk.FirstName, | |||||
| LastName = userk.LastName, | |||||
| Username = userk.UserName, | |||||
| Token = model.Token, | |||||
| RefreshToken = model.RefreshToken | |||||
| } | |||||
| }; | |||||
| } | |||||
| if (DateTime.Now > storedRefreshToken.ExpiryDate) | |||||
| { | |||||
| return new RefreshTokenResultDto { Error = "This refresh token has expired" }; | |||||
| } | |||||
| if (storedRefreshToken.Invalidated) | |||||
| { | |||||
| return new RefreshTokenResultDto { Error = "This refresh token has been invalidated" }; | |||||
| } | |||||
| if (storedRefreshToken.JwtId != jti) | |||||
| { | |||||
| return new RefreshTokenResultDto { Error = "This refresh token does not match this JWT" }; | |||||
| } | |||||
| storedRefreshToken.ExpiryDate = DateTime.Now.AddMinutes(_authSettings.JwtRefreshExpiredTime); | |||||
| await _databaseContext.SaveChangesAsync(); | |||||
| var user = await _userManager.FindByIdAsync(validatedToken.Claims.Single(x => x.Type == "id").Value); | |||||
| var token = await GenerateJwtToken(user); | |||||
| _logger.LogInformation($"Refresh token : {model.Token}"); | |||||
| return new RefreshTokenResultDto | |||||
| { | |||||
| Data = new AuthenticateResponseDto | |||||
| { | |||||
| Id = userk.Id, | |||||
| FirstName = userk.FirstName, | |||||
| LastName = userk.LastName, | |||||
| Username = userk.UserName, | |||||
| Token = token, | |||||
| RefreshToken = token | |||||
| } | |||||
| }; | |||||
| } | |||||
| public async Task<ServiceResponseDTO<string>> DeleteRefreshToken(int userId) | |||||
| { | |||||
| var refreshToken = await _databaseContext.RefreshTokens.Where(r => r.UserId == userId).FirstOrDefaultAsync(); | |||||
| if (refreshToken is null) | |||||
| return new ServiceResponseDTO<string> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "There is no refresh token for user" | |||||
| }; | |||||
| _databaseContext.RefreshTokens.Remove(refreshToken); | |||||
| var result = await _databaseContext.SaveChangesAsync() > 0; | |||||
| if (!result) | |||||
| return new ServiceResponseDTO<string> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "Problem with saving changes into database" | |||||
| }; | |||||
| _logger.LogInformation($"Delted refresh token : {refreshToken}"); | |||||
| return new ServiceResponseDTO<string> | |||||
| { | |||||
| Data = null | |||||
| }; | |||||
| } | |||||
| private ClaimsPrincipal? GetPrincipalFromToken(string token, bool validateLifetime) | |||||
| { | |||||
| var tokenHandler = new JwtSecurityTokenHandler(); | |||||
| var key = Encoding.ASCII.GetBytes(_authSettings.Secret); | |||||
| var tokenValidationParameters = new TokenValidationParameters | |||||
| { | |||||
| ValidateIssuerSigningKey = true, | |||||
| IssuerSigningKey = new SymmetricSecurityKey(key), | |||||
| ValidateIssuer = false, | |||||
| ValidateAudience = false, | |||||
| RequireExpirationTime = false, | |||||
| ValidateLifetime = validateLifetime, | |||||
| // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later) | |||||
| //ClockSkew = TimeSpan.Zero | |||||
| }; | |||||
| try | |||||
| { | |||||
| var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken); | |||||
| if (!IsJwtWithValidSecurityAlgorithm(validatedToken)) | |||||
| { | |||||
| return null; | |||||
| } | |||||
| return principal; | |||||
| } | |||||
| catch (Exception) | |||||
| { | |||||
| return null; | |||||
| } | |||||
| } | |||||
| private static bool IsJwtWithValidSecurityAlgorithm(SecurityToken validatedToken) | |||||
| { | |||||
| return (validatedToken is JwtSecurityToken jwtSecurityToken) && | |||||
| jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, | |||||
| StringComparison.InvariantCultureIgnoreCase); | |||||
| } | |||||
| public async Task<RefreshToken?> GetRefreshTokenByUserId(int userId) | |||||
| { | |||||
| return await _databaseContext.RefreshTokens.Where(x => x.UserId == userId).FirstOrDefaultAsync(); | |||||
| } | |||||
| public async Task UpdateRefreshToken(RefreshToken refreshToken) | |||||
| { | |||||
| _databaseContext.RefreshTokens.Update(refreshToken); | |||||
| await _databaseContext.SaveChangesAsync(); | |||||
| } | |||||
| public async Task<ServiceResponseDTO<object>> GetForgotPasswordUrlAsync(string email) | |||||
| { | |||||
| _logger.LogInformation($"Sending forgot password email for : {email}"); | |||||
| var user = await _userManager.FindByEmailAsync(email); | |||||
| if (user == null) | |||||
| { | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "Email did not find." | |||||
| }; | |||||
| } | |||||
| var token = await _userManager.GeneratePasswordResetTokenAsync(user); | |||||
| token = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(token)); | |||||
| await _emailer.SendEmailAndWriteToDbAsync(email, "Reset password", HTMLHelper.RenderForgotPasswordPage($"{_frontEndSettings.BaseUrl}/reset-password?token={token}&email={email}"), isHtml: true); | |||||
| _logger.LogInformation($"Reset password email is sent"); | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| Data = new { code = token, email = email } | |||||
| }; | |||||
| } | |||||
| public async Task<ServiceResponseDTO<object>> PasswordResetAsync(string email, string code, string password) | |||||
| { | |||||
| _logger.LogInformation($"User with email : {email} changes password"); | |||||
| var user = await _userManager.FindByEmailAsync(email); | |||||
| if (user == null) | |||||
| { | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "Email did not find." | |||||
| }; | |||||
| } | |||||
| var passwordResetToken = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); | |||||
| IdentityResult resetResult = await _userManager.ResetPasswordAsync(user, passwordResetToken, password); | |||||
| if (resetResult.Succeeded) | |||||
| { | |||||
| _logger.LogInformation($"Password for user : {email} changed successfully"); | |||||
| return new ServiceResponseDTO<object> { Data = true }; | |||||
| } | |||||
| var errors = resetResult.Errors.Select(x => x.Description); | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = errors.First() | |||||
| }; | |||||
| } | |||||
| public async Task<ServiceResponseDTO<object>> Register(RegisterDTO model) | |||||
| { | |||||
| _logger.LogInformation($"User with email: {model.Email} is going to register."); | |||||
| var user = await _userManager.FindByEmailAsync(model.Email); | |||||
| if (user == null) | |||||
| { | |||||
| _logger.LogInformation($"User with email: {model.Email} not found."); | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "User not invited." | |||||
| }; | |||||
| } | |||||
| _logger.LogInformation($"Found user: {user.FirstName} {user.LastName}"); | |||||
| _mapper.Map<RegisterDTO, User>(model, user); | |||||
| _logger.LogInformation($"Enabled login for user: {user.FirstName} {user.LastName}"); | |||||
| user.IsEnabled = true; | |||||
| IdentityResult resetResult = await _userManager.ResetPasswordAsync(user, HttpUtility.UrlDecode(model.Token), model.Password); | |||||
| if (resetResult.Succeeded) | |||||
| { | |||||
| _logger.LogInformation($"Succesfuly registered user: {user.FirstName} {user.LastName}"); | |||||
| await _databaseContext.SaveChangesAsync(); | |||||
| //_logger.LogInformation($"Password for user : {model.Email} changed successfully"); | |||||
| return new ServiceResponseDTO<object> { Data = true }; | |||||
| } | |||||
| var errors = resetResult.Errors.Select(x => x.Description); | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = errors.First() | |||||
| }; | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class CommentService : ICommentService | |||||
| { | |||||
| private readonly FrontEndSettings _frontEndSettings; | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly ILogger<CommentService> _logger; | |||||
| private readonly IEmailer _emailer; | |||||
| public CommentService(IOptions<FrontEndSettings> frontEndSettings,DatabaseContext context, IMapper mapper, ILogger<CommentService> logger,IEmailer emailer) | |||||
| { | |||||
| _frontEndSettings = frontEndSettings.Value; | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| _logger = logger; | |||||
| _emailer = emailer; | |||||
| } | |||||
| public async Task CreateComment(CommentCreateDto commentCreateDto) | |||||
| { | |||||
| _logger.LogInformation("Start creating comment"); | |||||
| var comment = _mapper.Map<Comment>(commentCreateDto); | |||||
| if(commentCreateDto.UsersToNotify.Count > 0) | |||||
| { | |||||
| _logger.LogInformation("Start sending emails"); | |||||
| await _emailer.SendEmailAsync(commentCreateDto.UsersToNotify, "You're tagged in comment by another user", | |||||
| HTMLHelper.RenderTagPage($"{_frontEndSettings.BaseUrl}/candidates/{commentCreateDto.ApplicantId}"), isHtml: true); | |||||
| _logger.LogInformation("Emails send successfully"); | |||||
| } | |||||
| comment.DateOfSending = DateTime.Now; | |||||
| _logger.LogInformation($"Comment created successfully in {comment.DateOfSending}"); | |||||
| _logger.LogInformation($"Saving comment in Db"); | |||||
| await _context.Comments.AddAsync(comment); | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Comment saved in Db"); | |||||
| await result; | |||||
| } | |||||
| } | |||||
| } |
| using System.Net.Mail; | |||||
| using System.Net; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| /// <summary> | |||||
| /// Provieds an API for sending emails in both sync & async fashion, as well as sending emails with delays. | |||||
| /// </summary> | |||||
| public class Emailer : IEmailer | |||||
| { | |||||
| private readonly MailSettings _settings; | |||||
| private readonly ILogger<Emailer> _logger; | |||||
| public Emailer(IOptions<MailSettings> mailSettings, ILogger<Emailer> logger) | |||||
| { | |||||
| _settings = mailSettings.Value; | |||||
| _logger = logger; | |||||
| } | |||||
| /// <summary> | |||||
| /// Sends an email asynchronously and inserts a new <see cref="DiligEmail"/> record to the underlying database. | |||||
| /// </summary> | |||||
| /// <param name="to"></param> | |||||
| /// <param name="subject"></param> | |||||
| /// <param name="body"></param> | |||||
| /// <param name="isHtml"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <param name="cc"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <see cref="ArgumentNullException"/> | |||||
| /// <exception cref="Exception"></exception> | |||||
| /// <returns></returns> | |||||
| public async Task<bool> SendEmailAndWriteToDbAsync(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null) | |||||
| { | |||||
| _logger.LogInformation($"Start sending email to {to}"); | |||||
| var emailResult = await SendEmailAsync(to, subject, body, isHtml, cc); | |||||
| //_logger.LogInformation("Create email entity for save in db"); | |||||
| var email = CreateEmail(to, subject, body, isHtml); | |||||
| if (emailResult) | |||||
| { | |||||
| email.SentTime = DateTime.Now; | |||||
| } | |||||
| //_logger.LogInformation("Save email in db"); | |||||
| //var dbResult = await WriteEmailToDbAsync(email); | |||||
| //_logger.LogInformation("Email is saved in db."); | |||||
| return emailResult; // && dbResult > 0; | |||||
| } | |||||
| public async Task<bool> SendEmailAndWriteToDbAsync(string to, string subject, string body, bool isHtml = false, List<string> cc = null) | |||||
| { | |||||
| return await SendEmailAndWriteToDbAsync(new List<string> { to }, subject, body, isHtml, cc); | |||||
| } | |||||
| /// <summary> | |||||
| /// Sends an email synchronously and inserts a new <see cref="DiligEmail"/> record to the underlying database. | |||||
| /// </summary> | |||||
| /// <param name="to"></param> | |||||
| /// <param name="subject"></param> | |||||
| /// <param name="body"></param> | |||||
| /// <param name="isHtml"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <param name="cc"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <see cref="ArgumentNullException"/> | |||||
| /// <exception cref="Exception"></exception> | |||||
| /// <returns></returns> | |||||
| public bool SendEmailAndWriteToDb(List<string> to, string subject, string body, bool isHtml = false, | |||||
| List<string> cc = null) | |||||
| { | |||||
| var emailResult = SendEmail(to, subject, body, isHtml, cc); | |||||
| var email = CreateEmail(to, subject, body, isHtml); | |||||
| if (emailResult) | |||||
| { | |||||
| email.SentTime = DateTime.Now; | |||||
| } | |||||
| //var dbResult = WriteEmailToDb(email); | |||||
| return emailResult; // && dbResult > 0; | |||||
| } | |||||
| public bool SendEmailAndWriteToDb(string to, string subject, string body, bool isHtml = false, | |||||
| List<string> cc = null) | |||||
| { | |||||
| return SendEmailAndWriteToDb(new List<string> { to }, subject, body, isHtml, cc); | |||||
| } | |||||
| /// <summary> | |||||
| /// Sends an email synchronously. | |||||
| /// </summary> | |||||
| /// <param name="to"></param> | |||||
| /// <param name="subject"></param> | |||||
| /// <param name="body"></param> | |||||
| /// <param name="isHtml"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <param name="cc"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <see cref="ArgumentNullException"/> | |||||
| /// <exception cref="Exception"></exception> | |||||
| /// <returns></returns> | |||||
| public bool SendEmail(List<string> to, string subject, string body, bool isHtml = false, | |||||
| List<string> cc = null) | |||||
| { | |||||
| try | |||||
| { | |||||
| using (var smtp = GetSmtpClient()) | |||||
| { | |||||
| var message = GetMailMessage(to, subject, body, isHtml, cc); | |||||
| smtp.Send(message); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| catch (ArgumentException) | |||||
| { | |||||
| throw; | |||||
| } | |||||
| catch (Exception e) | |||||
| { | |||||
| throw new Exception("Failed to send email message.", e); | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Sends an email asynchronously. | |||||
| /// </summary> | |||||
| /// <param name="to"></param> | |||||
| /// <param name="subject"></param> | |||||
| /// <param name="body"></param> | |||||
| /// <param name="isHtml"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <param name="cc"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <see cref="ArgumentNullException"/> | |||||
| /// <exception cref="Exception"></exception> | |||||
| /// <returns></returns> | |||||
| public async Task<bool> SendEmailAsync(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null) | |||||
| { | |||||
| try | |||||
| { | |||||
| _logger.LogInformation("Getting SMTP client settings from appsettings file"); | |||||
| using (var smtp = GetSmtpClient()) | |||||
| { | |||||
| _logger.LogInformation("Create mail message"); | |||||
| var message = GetMailMessage(to, subject, body, isHtml, cc); | |||||
| _logger.LogInformation("Message created."); | |||||
| _logger.LogInformation("Sending message to client"); | |||||
| await smtp.SendMailAsync(message); | |||||
| _logger.LogInformation("Email message sent."); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| catch (ArgumentException ex) | |||||
| { | |||||
| _logger.LogInformation($"Error in arguments {ex}"); | |||||
| throw; | |||||
| } | |||||
| catch (Exception e) | |||||
| { | |||||
| _logger.LogInformation($"Error {e}"); | |||||
| throw new Exception("Failed to send email message.", e); | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Creates a <see cref="SmtpClient"/> object and configures it. | |||||
| /// </summary> | |||||
| /// <returns></returns> | |||||
| public SmtpClient GetSmtpClient() | |||||
| { | |||||
| var smtp = new SmtpClient(_settings.SmtpServer) { Timeout = 1000000 }; | |||||
| if (!string.IsNullOrWhiteSpace(_settings.SmtpUsername)) | |||||
| { | |||||
| smtp.UseDefaultCredentials = false; | |||||
| smtp.Credentials = new NetworkCredential( | |||||
| _settings.SmtpUsername, | |||||
| _settings.SmtpPassword); | |||||
| smtp.EnableSsl = _settings.SmtpUseSSL; | |||||
| smtp.Port = _settings.SmtpPort; | |||||
| } | |||||
| return smtp; | |||||
| } | |||||
| /// <summary> | |||||
| /// Creates a new <see cref="MailMessage"/> from the specified arguments. | |||||
| /// </summary> | |||||
| /// <param name="to"></param> | |||||
| /// <param name="subject"></param> | |||||
| /// <param name="body"></param> | |||||
| /// <param name="isHtml"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <param name="cc"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <returns></returns> | |||||
| public MailMessage GetMailMessage(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null) | |||||
| { | |||||
| var message = new MailMessage | |||||
| { | |||||
| Sender = new MailAddress(_settings.SmtpFrom, _settings.SmtpFromName), | |||||
| From = new MailAddress(_settings.SmtpFrom), | |||||
| Subject = subject, | |||||
| Body = body, | |||||
| IsBodyHtml = isHtml | |||||
| }; | |||||
| if (to.Any()) | |||||
| { | |||||
| message.To.Add(string.Join(",", to.Where(email => !string.IsNullOrWhiteSpace(email)))); | |||||
| } | |||||
| else | |||||
| { | |||||
| throw new ArgumentException("The list of recipient emails can not be empty"); | |||||
| } | |||||
| if (cc != null && cc.Any()) | |||||
| { | |||||
| message.CC.Add(string.Join(",", cc.Where(email => !string.IsNullOrWhiteSpace(email)))); | |||||
| } | |||||
| return message; | |||||
| } | |||||
| /// <summary> | |||||
| /// Sends an email aysnchronously. If the "dont send before" argument is specified, then the email will be sent with a delay - after the specified time. | |||||
| /// </summary> | |||||
| /// <param name="to"></param> | |||||
| /// <param name="subject"></param> | |||||
| /// <param name="body"></param> | |||||
| /// <param name="isHtml"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <param name="dontSendBefore"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <exception cref="Exception"></exception> | |||||
| /// <returns></returns> | |||||
| //public async Task<bool> SendEmailWithDelayAsync(List<string> to, string subject, string body, bool isHtml = false, DateTime? dontSendBefore = null) | |||||
| //{ | |||||
| // try | |||||
| // { | |||||
| // var email = CreateEmail(to, subject, body, isHtml, dontSendBefore); | |||||
| // //var result = await WriteEmailToDbAsync(email); | |||||
| // return true; | |||||
| // } | |||||
| // catch (ArgumentException) | |||||
| // { | |||||
| // throw; | |||||
| // } | |||||
| // catch (Exception e) | |||||
| // { | |||||
| // throw new Exception("Error while attempting to send an email with delay.", e); | |||||
| // } | |||||
| //} | |||||
| /// <summary> | |||||
| /// Creates a <see cref="DiligEmail"/> object with specified arguments. | |||||
| /// </summary> | |||||
| /// <param name="to"></param> | |||||
| /// <param name="subject"></param> | |||||
| /// <param name="body"></param> | |||||
| /// <param name="isHtml"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <param name="dontSendBefore"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <returns></returns> | |||||
| public DiligEmail CreateEmail(List<string> to, string subject, string body, bool isHtml = false, | |||||
| DateTime? dontSendBefore = null) | |||||
| { | |||||
| if (!to.Any()) | |||||
| { | |||||
| throw new ArgumentException("The list of recipient emails can not be empty"); | |||||
| } | |||||
| var email = new DiligEmail | |||||
| { | |||||
| To = to.Aggregate((previous, next) => previous + ";" + next), | |||||
| Subject = subject, | |||||
| Body = body, | |||||
| IsHtml = isHtml, | |||||
| DontSendBefore = dontSendBefore, | |||||
| CreateTime = DateTime.Now | |||||
| }; | |||||
| return email; | |||||
| } | |||||
| /// <summary> | |||||
| /// Fills the specified <see cref="DiligEmail"/> object with the specified <see cref="EmailAttachment"/> list. | |||||
| /// </summary> | |||||
| /// <param name="email"></param> | |||||
| /// <param name="attachments"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <returns></returns> | |||||
| //public DiligEmail FillEmailAttachments(DiligEmail email) | |||||
| //{ | |||||
| // if (email == null) | |||||
| // { | |||||
| // throw new ArgumentNullException(nameof(email), "Email can not be null"); | |||||
| // } | |||||
| // if (attachments != null && attachments.Any()) | |||||
| // { | |||||
| // attachments.ForEach(attachment => | |||||
| // { | |||||
| // email.DiligEmailAttachments.Add(new DiligEmailAttachment | |||||
| // { | |||||
| // FileName = attachment.FileName, | |||||
| // SourceFileName = attachment.SourceFileName, | |||||
| // Type = attachment.Type, | |||||
| // Disposition = attachment.Disposition | |||||
| // }); | |||||
| // }); | |||||
| // } | |||||
| // return email; | |||||
| //} | |||||
| /// <summary> | |||||
| /// Writes the specified <see cref="DiligEmail"/> object to the underlying database. | |||||
| /// </summary> | |||||
| /// <param name="email"></param> | |||||
| /// <exception cref="ArgumentException"></exception> | |||||
| /// <exception cref="Exception"></exception> | |||||
| /// <returns></returns> | |||||
| //public async Task<int> WriteEmailToDbAsync(DiligEmail email) | |||||
| //{ | |||||
| // try | |||||
| // { | |||||
| // if (email == null) | |||||
| // { | |||||
| // throw new ArgumentNullException(nameof(email), "Email can not be null"); | |||||
| // } | |||||
| // _entities.DiligEmails.Add(email); | |||||
| // var result = await _entities.SaveChangesAsync(); | |||||
| // return result; | |||||
| // } | |||||
| // catch (Exception e) | |||||
| // { | |||||
| // throw new Exception("Failed to write entry into the database", e); | |||||
| // } | |||||
| //} | |||||
| /// <summary> | |||||
| /// Writes the specified <see cref="DiligEmail"/> object to the underlying database. | |||||
| /// </summary> | |||||
| /// <param name="email"></param> | |||||
| /// <exception cref="ArgumentNullException"></exception> | |||||
| /// <exception cref="Exception"></exception> | |||||
| /// <returns></returns> | |||||
| //public int WriteEmailToDb(DiligEmail email) | |||||
| //{ | |||||
| // try | |||||
| // { | |||||
| // if (email == null) | |||||
| // { | |||||
| // throw new ArgumentNullException(nameof(email), "Email can not be null"); | |||||
| // } | |||||
| // _entities.DiligEmails.Add(email); | |||||
| // var result = _entities.SaveChanges(); | |||||
| // return result; | |||||
| // } | |||||
| // catch (ArgumentException) | |||||
| // { | |||||
| // throw; | |||||
| // } | |||||
| // catch (Exception e) | |||||
| // { | |||||
| // throw new Exception("Failed to write entry into the database", e); | |||||
| // } | |||||
| //} | |||||
| } | |||||
| } |
| using Microsoft.AspNetCore.Http; | |||||
| using Microsoft.Extensions.Configuration; | |||||
| using Microsoft.WindowsAzure.Storage; | |||||
| using Microsoft.WindowsAzure.Storage.Blob; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class FileService : IFileService | |||||
| { | |||||
| private readonly IConfiguration _configuration; | |||||
| public FileService(IConfiguration configuration) | |||||
| { | |||||
| _configuration = configuration; | |||||
| } | |||||
| public async Task<string> GetCV(string fileName) | |||||
| { | |||||
| await using MemoryStream memoryStream = new(); | |||||
| var cloudBlockBlob = GetCloudBlockBlob(fileName); | |||||
| await cloudBlockBlob.DownloadToStreamAsync(memoryStream); | |||||
| Stream blobStream = cloudBlockBlob.OpenReadAsync().Result; | |||||
| return ConvertToBase64(blobStream); | |||||
| } | |||||
| public async Task UploadCV(string fileName,IFormFile file) | |||||
| { | |||||
| var cloudBlockBlob = GetCloudBlockBlob(fileName); | |||||
| await using var data = file.OpenReadStream(); | |||||
| await cloudBlockBlob.UploadFromStreamAsync(data); | |||||
| } | |||||
| private CloudBlockBlob GetCloudBlockBlob(string fileName) | |||||
| { | |||||
| string blobstorageconnection = _configuration.GetValue<string>("BlobConnectionString"); | |||||
| CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(blobstorageconnection); | |||||
| CloudBlobClient blobClient = cloudStorageAccount.CreateCloudBlobClient(); | |||||
| CloudBlobContainer container = blobClient.GetContainerReference( | |||||
| _configuration.GetValue<string>("BlobContainerName")); | |||||
| return container.GetBlockBlobReference(fileName); | |||||
| } | |||||
| private static string ConvertToBase64(Stream stream) | |||||
| { | |||||
| byte[] bytes; | |||||
| using (var memoryStream = new MemoryStream()) | |||||
| { | |||||
| stream.CopyTo(memoryStream); | |||||
| bytes = memoryStream.ToArray(); | |||||
| } | |||||
| string base64 = Convert.ToBase64String(bytes); | |||||
| return base64; | |||||
| } | |||||
| } | |||||
| } |
| using System.Net; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class HttpClientService : IHttpClientService | |||||
| { | |||||
| private const string GoogleApiTokenInfoUrl = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={0}"; | |||||
| private string[] SupportedClientsIds = { "" }; | |||||
| private readonly AuthorizationSettings _authSettings; | |||||
| private readonly ILogger<HttpClientService> _logger; | |||||
| public HttpClientService(IOptions<AuthorizationSettings> authSettings, ILogger<HttpClientService> logger) | |||||
| { | |||||
| _authSettings = authSettings.Value; | |||||
| _logger = logger; | |||||
| } | |||||
| public async Task<bool> IsTokenValid(string providerToken) | |||||
| { | |||||
| _logger.LogInformation($"Start checking is token valid: {providerToken}"); | |||||
| var httpClient = new HttpClient(); | |||||
| var requestUri = new Uri(string.Format(GoogleApiTokenInfoUrl, providerToken)); | |||||
| _logger.LogInformation("Initilazing http call to googleapi"); | |||||
| HttpResponseMessage httpResponseMessage; | |||||
| try | |||||
| { | |||||
| _logger.LogInformation("Calling googleapi HTTPGet method"); | |||||
| httpResponseMessage = httpClient.GetAsync(requestUri).Result; | |||||
| } | |||||
| catch(Exception ex) | |||||
| { | |||||
| _logger.LogInformation($"Error in call: {ex.Message}"); | |||||
| return false; | |||||
| } | |||||
| if (httpResponseMessage.StatusCode != HttpStatusCode.OK) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| var response = httpResponseMessage.Content.ReadAsStringAsync().Result; | |||||
| var googleApiTokenInfo = JsonConvert.DeserializeObject<GoogleApiTokenInfo>(response); | |||||
| _logger.LogInformation($"Call pass and it received: {googleApiTokenInfo}"); | |||||
| //if (!SupportedClientsIds.Contains(googleApiTokenInfo.aud)) | |||||
| if (googleApiTokenInfo.aud != _authSettings.GoogleClientId) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } |
| using Microsoft.AspNetCore.Http; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class ImportService : IImportService | |||||
| { | |||||
| private readonly ILogger<ImportService> _logger; | |||||
| private readonly ISaveImportedDataService _service; | |||||
| public ImportService(ILogger<ImportService> logger, ISaveImportedDataService service) | |||||
| { | |||||
| _logger = logger; | |||||
| _service = service; | |||||
| } | |||||
| public async Task<List<ApplicantImportDto>> Import(IFormFile fileData) | |||||
| { | |||||
| try | |||||
| { | |||||
| using (var stream = new MemoryStream()) | |||||
| { | |||||
| fileData.CopyTo(stream); | |||||
| var FileData = stream.ToArray(); | |||||
| CopyFileToDirectory(FileData); | |||||
| } | |||||
| return await _service.Save(); | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| throw new Exception(ex.Message); | |||||
| } | |||||
| return new List<ApplicantImportDto>(); | |||||
| } | |||||
| private async Task CopyFileToDirectory(byte[] FileData) | |||||
| { | |||||
| try | |||||
| { | |||||
| var content = new System.IO.MemoryStream(FileData); | |||||
| var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "s.xlsx"); | |||||
| await CopyStream(content, path); | |||||
| } | |||||
| catch (Exception) | |||||
| { | |||||
| throw; | |||||
| } | |||||
| } | |||||
| private async Task CopyStream(Stream stream, string downloadPath) | |||||
| { | |||||
| using (var fileStream = new FileStream(downloadPath, FileMode.Create, FileAccess.Write)) | |||||
| { | |||||
| await stream.CopyToAsync(fileStream); | |||||
| } | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class InsuranceCompaniesService : IInsuranceCompaniesService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| public InsuranceCompaniesService(DatabaseContext context, IMapper mapper) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| } | |||||
| public async Task<List<InsuranceCompanyViewDto>> GetInsuranceCompanies() | |||||
| { | |||||
| var insuranceCompanies = await _context.InsuranceCompanies.ToListAsync(); | |||||
| var insuranceCompaniesDto = _mapper.Map<List<InsuranceCompanyViewDto>>(insuranceCompanies); | |||||
| return insuranceCompaniesDto; | |||||
| } | |||||
| public async Task<InsuranceCompanyViewDto?> GetInsuranceCompany(long id) | |||||
| { | |||||
| var insuranceCompany = await _context.InsuranceCompanies.FindAsync(id); | |||||
| if (insuranceCompany == null) | |||||
| throw new EntityNotFoundException("Insurance company not found"); | |||||
| var insuranceCompanyDto = _mapper.Map<InsuranceCompanyViewDto?>(insuranceCompany); | |||||
| return insuranceCompanyDto; | |||||
| } | |||||
| public async Task CreateInsuranceCompany(InsuranceCompanyCreateDto insuranceCompanyCreateDto) | |||||
| { | |||||
| var insuranceCompany = _mapper.Map<InsuranceCompany>(insuranceCompanyCreateDto); | |||||
| insuranceCompany.CreatedAtUtc = DateTime.Now; | |||||
| await _context.InsuranceCompanies.AddAsync(insuranceCompany); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| public async Task UpdateInsuranceCompany(long insuranceCompanyId, InsuranceCompanyUpdateDto insuranceCompanyUpdateDto) | |||||
| { | |||||
| var insuranceCompany = _context.InsuranceCompanies.Find(insuranceCompanyId); | |||||
| if (insuranceCompany == null) | |||||
| throw new EntityNotFoundException($"Insurance company not found"); | |||||
| _mapper.Map(insuranceCompanyUpdateDto, insuranceCompany); | |||||
| insuranceCompany.UpdatedAtUtc = DateTime.Now; | |||||
| _context.Entry(insuranceCompany).State = EntityState.Modified; | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| public async Task DeleteInsuranceCompany(long insuranceCompanyId) | |||||
| { | |||||
| var insuranceCompany = _context.InsuranceCompanies.Find(insuranceCompanyId); | |||||
| if (insuranceCompany == null) | |||||
| throw new EntityNotFoundException("Insurance company not found"); | |||||
| _context.Remove(insuranceCompany); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class InsurancePoliciesService : IInsurancePoliciesService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IWebhookPublisherService _webhookPublisher; | |||||
| private readonly IMapper _mapper; | |||||
| public InsurancePoliciesService(DatabaseContext context, IWebhookPublisherService webhookPublisher, IMapper mapper) | |||||
| { | |||||
| _context = context; | |||||
| _webhookPublisher = webhookPublisher; | |||||
| _mapper = mapper; | |||||
| } | |||||
| public async Task<List<InsurancePolicyViewDto>> GetInsurancePolicies() | |||||
| { | |||||
| var insurancePolicies = await _context.InsurancePolicies | |||||
| .Include(i => i.Insurer) | |||||
| .ThenInclude(k => k.InsuranceCompany) | |||||
| .ToListAsync(); | |||||
| var insurancePoliciesDto = _mapper.Map<List<InsurancePolicyViewDto>>(insurancePolicies); | |||||
| return insurancePoliciesDto; | |||||
| } | |||||
| public async Task<InsurancePolicyViewDto?> GetInsurancePolicy(long id) | |||||
| { | |||||
| var insurancePolicy = await _context.InsurancePolicies | |||||
| .Include(i => i.Insurer) | |||||
| .ThenInclude(k => k.InsuranceCompany) | |||||
| .FirstOrDefaultAsync(i => i.Id == id); | |||||
| if (insurancePolicy == null) | |||||
| throw new EntityNotFoundException("Insurance policy not found"); | |||||
| var insurancePolicyDto = _mapper.Map<InsurancePolicyViewDto>(insurancePolicy); | |||||
| return insurancePolicyDto; | |||||
| } | |||||
| public async Task CreateInsurancePolicy(InsurancePolicyCreateDto insurancePolicyCreateDto) | |||||
| { | |||||
| var insurancePolicy = _mapper.Map<InsurancePolicy>(insurancePolicyCreateDto); | |||||
| var result = await _context.InsurancePolicies.AddAsync(insurancePolicy); | |||||
| await _webhookPublisher.PublishAsync("insurancePolicy.created", result); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| public async Task UpdateInsurancePolicy(long insurancePolicyId, InsurancePolicyUpdateDto insurancePolicyUpdateDto) | |||||
| { | |||||
| var insurancePolicy = _context.InsurancePolicies.Find(insurancePolicyId); | |||||
| if (insurancePolicy == null) | |||||
| throw new EntityNotFoundException("Insurance policy not found"); | |||||
| _mapper.Map(insurancePolicyUpdateDto, insurancePolicy); | |||||
| insurancePolicy.UpdatedAtUtc = DateTime.Now; | |||||
| _context.Entry(insurancePolicy).State = EntityState.Modified; | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| public async Task DeleteInsurancePolicy(long insurancePolicyId) | |||||
| { | |||||
| var insurancePolicy = _context.InsurancePolicies.Find(insurancePolicyId); | |||||
| if (insurancePolicy == null) | |||||
| throw new EntityNotFoundException("Insurance policy not found"); | |||||
| _context.Remove(insurancePolicy); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class InsurersService : IInsurersService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| public InsurersService(DatabaseContext context, IMapper mapper) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| } | |||||
| public async Task<List<InsurerViewDto>> GetInsurers() | |||||
| { | |||||
| var insurers = await _context.Insurers.Include(x => x.InsuranceCompany).ToListAsync(); | |||||
| return _mapper.Map<List<InsurerViewDto>>(insurers); | |||||
| } | |||||
| public async Task<InsurerViewDto?> GetInsurer(long id) | |||||
| { | |||||
| var insurer = await _context.Insurers | |||||
| .Include(x => x.InsuranceCompany) | |||||
| .FirstOrDefaultAsync(x => x.Id == id); | |||||
| if (insurer == null) | |||||
| throw new EntityNotFoundException("Insurer not found"); | |||||
| return _mapper.Map<InsurerViewDto>(insurer); | |||||
| } | |||||
| public async Task CreateInsurer(InsurerCreateDto insurerCreateDto) | |||||
| { | |||||
| var insurer = _mapper.Map<Insurer>(insurerCreateDto); | |||||
| await _context.Insurers.AddAsync(insurer); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| public async Task UpdateInsurer(long insurerId, InsurerUpdateDto insurerUpdateDto) | |||||
| { | |||||
| var insurer = _context.InsurancePolicies.Find(insurerId); | |||||
| if (insurer == null) | |||||
| throw new EntityNotFoundException("Insurer not found"); | |||||
| _mapper.Map(insurerUpdateDto, insurer); | |||||
| insurer.UpdatedAtUtc = DateTime.Now; | |||||
| _context.Entry(insurer).State = EntityState.Modified; | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| public async Task DeleteInsurerAsync(long insurerId) | |||||
| { | |||||
| var insurer = _context.InsurancePolicies.Find(insurerId); | |||||
| if (insurer == null) | |||||
| throw new EntityNotFoundException("Insurer not found"); | |||||
| _context.Remove(insurer); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| } | |||||
| } |
| | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IAdService | |||||
| { | |||||
| Task<List<AdResponseDto>> GetAllAsync(); | |||||
| Task<List<AdResponseWithCountDto>> GetAllWithCountAsync(); | |||||
| Task<AdResponseDto> GetByIdAsync(int id); | |||||
| Task<AdDetailsResponseDto> GetAdDetailsByIdAsync(int id); | |||||
| Task<List<AdResponseDto>> GetArchiveAds(); | |||||
| Task<List<AdResponseDto>> GetFilteredAdsAsync(AdFilterDto filters); | |||||
| Task CreateAsync(AdCreateDto adCreateDto); | |||||
| Task<Ad> ImportAsync(AdCreateDto adCreateDto); | |||||
| Task UpdateAsync(int id, AdUpdateDto adUpdateDto); | |||||
| Task ArchiveAdAsync(int id); | |||||
| Task DeleteAsync(int id); | |||||
| Task<Ad> GetByIdEntityAsync(int id); | |||||
| } | |||||
| } |
| | |||||
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IApplicantService | |||||
| { | |||||
| Task<QueryResultDto<ApplicantViewDto>> GetFilteredApplicants(ApplicantFilterDto applicantFilterDto); | |||||
| Task<List<AdApplicantsViewDto>> GetAllAdsApplicants(ApplicantFilterDto applicantFilterDto); | |||||
| Task<ApplicantViewDto> GetById(int id); | |||||
| Task<ApplicantViewDto> GetApplicantWithSelectionProcessesById(int id); | |||||
| Task ApplyForAd(ApplyForAdRequestDto request); | |||||
| Task DeleteApplicant(int id); | |||||
| Task<List<ApplicantOptionsDTO>> GetOptions(); | |||||
| Task<ServiceResponseDTO<object>> InitializeProcess(ApplicantProcessRequestDTO model); | |||||
| Task ImportApplicant(List<ApplicantImportDto> request); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IAuthenticationService | |||||
| { | |||||
| Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(AuthenticateRequestDto model); | |||||
| Task<ServiceResponseDTO<AuthenticateResponseDto>> Authenticate(GoogleApiModel model); | |||||
| Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model); | |||||
| Task<RefreshToken?> GetRefreshTokenByUserId(int userId); | |||||
| Task UpdateRefreshToken(RefreshToken refreshToken); | |||||
| Task<ServiceResponseDTO<string>> DeleteRefreshToken(int userId); | |||||
| Task<ServiceResponseDTO<object>> GetForgotPasswordUrlAsync(string email); | |||||
| Task<ServiceResponseDTO<object>> PasswordResetAsync(string email, string code, string password); | |||||
| Task<ServiceResponseDTO<object>> Register(RegisterDTO model); | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Comment; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface ICommentService | |||||
| { | |||||
| Task CreateComment(CommentCreateDto commentCreateDto); | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.Models; | |||||
| using System.Net.Mail; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IEmailer | |||||
| { | |||||
| DiligEmail CreateEmail(List<string> to, string subject, string body, bool isHtml = false, DateTime? dontSendBefore = null); | |||||
| MailMessage GetMailMessage(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null); | |||||
| SmtpClient GetSmtpClient(); | |||||
| bool SendEmail(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null); | |||||
| bool SendEmailAndWriteToDb(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null); | |||||
| bool SendEmailAndWriteToDb(string to, string subject, string body, bool isHtml = false, List<string> cc = null); | |||||
| Task<bool> SendEmailAndWriteToDbAsync(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null); | |||||
| Task<bool> SendEmailAndWriteToDbAsync(string to, string subject, string body, bool isHtml = false, List<string> cc = null); | |||||
| Task<bool> SendEmailAsync(List<string> to, string subject, string body, bool isHtml = false, List<string> cc = null); | |||||
| } | |||||
| } |
| using Microsoft.AspNetCore.Http; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IFileService | |||||
| { | |||||
| Task<string> GetCV(string fileName); | |||||
| Task UploadCV(string fileName,IFormFile file); | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IHttpClientService | |||||
| { | |||||
| Task<bool> IsTokenValid(string providedToken); | |||||
| } | |||||
| } |
| using Microsoft.AspNetCore.Http; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IImportService | |||||
| { | |||||
| Task<List<ApplicantImportDto>> Import(IFormFile fileData); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IInsuranceCompaniesService | |||||
| { | |||||
| Task<List<InsuranceCompanyViewDto>> GetInsuranceCompanies(); | |||||
| Task<InsuranceCompanyViewDto?> GetInsuranceCompany(long id); | |||||
| Task CreateInsuranceCompany(InsuranceCompanyCreateDto insuranceCompanyCreateDto); | |||||
| Task UpdateInsuranceCompany(long insuranceCompanyId, InsuranceCompanyUpdateDto insuranceCompanyUpdateDto); | |||||
| Task DeleteInsuranceCompany(long insuranceCompanyId); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IInsurancePoliciesService | |||||
| { | |||||
| Task<List<InsurancePolicyViewDto>> GetInsurancePolicies(); | |||||
| Task<InsurancePolicyViewDto?> GetInsurancePolicy(long id); | |||||
| Task CreateInsurancePolicy(InsurancePolicyCreateDto insurancePolicyCreateDto); | |||||
| Task UpdateInsurancePolicy(long insurancePolicyId, InsurancePolicyUpdateDto insurancePolicyUpdateDto); | |||||
| Task DeleteInsurancePolicy(long insurancePolicyId); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IInsurersService | |||||
| { | |||||
| Task<List<InsurerViewDto>> GetInsurers(); | |||||
| Task<InsurerViewDto?> GetInsurer(long id); | |||||
| Task CreateInsurer(InsurerCreateDto insurerCreateDto); | |||||
| Task UpdateInsurer(long insurerId, InsurerUpdateDto insurerUpdateDto); | |||||
| Task DeleteInsurerAsync(long insurerId); | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Pattern; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IPatternService | |||||
| { | |||||
| Task<List<PatternResponseDto>> GetAllAsync(); | |||||
| Task<PatternResponseDto> GetByIdAsync(int id); | |||||
| Task<List<PatternResponseDto>> GetFilteredPatternsAsync(FilterPatternDto filterPatternDto); | |||||
| Task<List<PatternApplicantViewDto>> GetCorrespondingPatternApplicants(int id); | |||||
| Task CreateAsync(PatternCreateDto patternCreateDto); | |||||
| Task<ScheduleInterviewResponseDto?> ScheduleIntrviewAsync(ScheduleInterviewDto scheduleInterviewDto); | |||||
| Task UpdateAsync(PatternUpdateDto patternUpdateDto, int id); | |||||
| Task DeleteAsync(int id); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface ISaveImportedDataService | |||||
| { | |||||
| Task<List<ApplicantImportDto>> Save(); | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Schedule; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IScheduleService | |||||
| { | |||||
| Task<List<ScheduleViewDto>> GetScheduleForCertainPeriod(int month, int year); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IScreeningTestClientService | |||||
| { | |||||
| Task<AuthSuccessResponse> LoginToScreening(AuthenticateRequestDto model); | |||||
| Task GetScreening(); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IScreeningTestService | |||||
| { | |||||
| Task<BaseResult<IEnumerable<TestMicroserviceRequest>>> GetScreening(); | |||||
| Task<bool> SendTest(TestMicroserviceInviteRequest test); | |||||
| Task<AuthSuccessResponse> LoginToScreening(); | |||||
| } | |||||
| } |
| | |||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionLevel; | |||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | |||||
| using Diligent.WebAPI.Contracts.DTOs.Stats; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface ISelectionLevelService | |||||
| { | |||||
| Task<List<SelectionLevelResponseWithDataDto>> GetAllAsync(); | |||||
| Task<SelectionLevelResposneDto> GetByIdAsync(int id); | |||||
| Task<SelectionLevel> GetByIdEntity(int id); | |||||
| List<SelectionLevelResponseWithDataDto> GetFilteredLevelsAsync(SelectionProcessFilterDto filters); | |||||
| Task<List<SelectionLevelInfoDto>> GetCountByLevels(List<string> statuses); | |||||
| } | |||||
| } |
| | |||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | |||||
| using Diligent.WebAPI.Contracts.DTOs.Stats; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface ISelectionProcessService | |||||
| { | |||||
| Task<List<SelectionProcessResposneDto>> GetAllAsync(); | |||||
| Task<bool> FinishSelectionProcess(SelectionProcessCreateDto model); | |||||
| Task UpdateSelectionProcessStatusAsync(int id, SelectionProcessUpdateStatusDto selectionProcessUpdateStatusDto); | |||||
| Task StatusUpdate(StatusChangeDTO model); | |||||
| Task InterviewerUpdate(InterviewerUpdateDTO model); | |||||
| } | |||||
| } |
| | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface ITechnologyService | |||||
| { | |||||
| Task<List<TechnologyResponseDto>> GetAllAsync(); | |||||
| Task<TechnologyResponseDto> GetByIdAsync(int id); | |||||
| Task<Technology> GetEntityByIdAsync(int id); | |||||
| Task<List<Technology>> GetEntitiesAsync(int [] technologiesIds); | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.User; | |||||
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IUserService | |||||
| { | |||||
| Task<IEnumerable<User?>> GetAll(); | |||||
| Task<User> GetById(int id); | |||||
| Task<User> GetByEmail(string email); | |||||
| Task CreateUser(CreateUserRequestDto model); | |||||
| Task<bool?> ToggleEnable(User user); | |||||
| Task RemoveUser(User user); | |||||
| Task<ServiceResponseDTO<object>> SendRegistrationLink(InviteDTO invite); | |||||
| Task<User> GetFirst(); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IWebhookDefinitionService | |||||
| { | |||||
| /// <summary> | |||||
| /// Get all webhook definitions | |||||
| /// </summary> | |||||
| Task<List<WebhookDefinitionViewDto>> GetAll(); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IWebhookPublisherService | |||||
| { | |||||
| Task PublishAsync(string webhookName, object data); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services.Interfaces | |||||
| { | |||||
| public interface IWebhookSubscriptionService | |||||
| { | |||||
| /// <summary> | |||||
| /// Create new webhook subscription | |||||
| /// </summary> | |||||
| Task<WebhookSubscription> CreateWebhookSubscription(WebhookSubscriptionCreateDto dto); | |||||
| /// <summary> | |||||
| /// Get subscriptions by webhook name | |||||
| /// </summary> | |||||
| Task<List<WebhookSubscription>> GetAllSubscriptionsAsync(string webhookName); | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Pattern; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class PatternService : IPatternService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly ISelectionLevelService _selectionLevelService; | |||||
| private readonly ILogger<PatternService> _logger; | |||||
| private readonly IEmailer _emailer; | |||||
| private readonly ISelectionProcessService _selectionProcessService; | |||||
| public PatternService(DatabaseContext context, IMapper mapper, ISelectionLevelService selectionLevelService, ILogger<PatternService> logger, IEmailer emailer, ISelectionProcessService selectionProcessService) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| _selectionLevelService = selectionLevelService; | |||||
| _logger = logger; | |||||
| _emailer = emailer; | |||||
| _selectionProcessService = selectionProcessService; | |||||
| } | |||||
| public async Task<List<PatternResponseDto>> GetAllAsync() | |||||
| { | |||||
| _logger.LogInformation("Start getting all Patterns"); | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var fromDb = await _context.Patterns.Include(x => x.SelectionLevel).ToListAsync(); | |||||
| _logger.LogInformation($"Received {fromDb.Count} patterns from db."); | |||||
| _logger.LogInformation($"Mapping received patterns to PatternResponseDto"); | |||||
| var result = _mapper.Map<List<PatternResponseDto>>(fromDb); | |||||
| _logger.LogInformation($"Patterns has been mapped and received to client: {result.Count} mapped patterns"); | |||||
| return result; | |||||
| } | |||||
| public async Task<PatternResponseDto> GetByIdAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Pattern with id = {id}"); | |||||
| var pattern = await _context.Patterns.Include(x => x.SelectionLevel).Where(x => x.Id == id).FirstOrDefaultAsync(); | |||||
| if (pattern is null) | |||||
| { | |||||
| _logger.LogError($"Pattern with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Pattern not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Pattern with id = {id}"); | |||||
| PatternResponseDto result = _mapper.Map<PatternResponseDto>(pattern); | |||||
| _logger.LogInformation($"Pattern with id = {id} mapped successfully"); | |||||
| return result; | |||||
| } | |||||
| public async Task<List<PatternResponseDto>> GetFilteredPatternsAsync(FilterPatternDto filterPatternDto) | |||||
| { | |||||
| _logger.LogInformation($"Start getting all filtered Patterns"); | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var filteredPatterns = await _context.Patterns.Include(x => x.SelectionLevel).ToListAsync(); | |||||
| _logger.LogInformation($"Received {filteredPatterns.Count} patterns from db."); | |||||
| _logger.LogInformation($"Mapping received patterns to PatternResponseDto"); | |||||
| List<PatternResponseDto> result = _mapper.Map<List<PatternResponseDto>>(filteredPatterns.FilterApplicants(filterPatternDto)); | |||||
| _logger.LogInformation($"Patterns has been mapped and received to client: {result.Count} mapped patterns"); | |||||
| return result; | |||||
| } | |||||
| public async Task<List<PatternApplicantViewDto>> GetCorrespondingPatternApplicants(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start getting corresponding pattern applicants"); | |||||
| _logger.LogInformation($"Getting pattern from database by id"); | |||||
| var pattern = await _context.Patterns.Include(x => x.SelectionLevel).ThenInclude(y => y.SelectionProcesses).ThenInclude(z => z.Applicant).Where(x => x.Id == id).FirstOrDefaultAsync(); | |||||
| if(pattern == null) | |||||
| { | |||||
| _logger.LogError($"Pattern with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Pattern not found"); | |||||
| } | |||||
| _logger.LogInformation($"Select applicants from selection processes with status \"Čeka na zakazivanje\""); | |||||
| var selectionProcessesApplicants = pattern.SelectionLevel.SelectionProcesses.Where(x => x.Status == "Čeka na zakazivanje").Select(x => x.Applicant).ToList(); | |||||
| _logger.LogInformation($"Mapping Pattern applicants"); | |||||
| var applicants = _mapper.Map<List<PatternApplicantViewDto>>(selectionProcessesApplicants); | |||||
| _logger.LogInformation($"Pattern applicants mapped successfully"); | |||||
| return applicants; | |||||
| } | |||||
| public async Task CreateAsync(PatternCreateDto patternCreateDto) | |||||
| { | |||||
| _logger.LogInformation($"Start creating Pattern"); | |||||
| _logger.LogInformation($"Check is Pattern in database"); | |||||
| var patternExists = await _context.Patterns.Include(x => x.SelectionLevel).Where(x => x.Title == patternCreateDto.Title && x.SelectionLevelId == patternCreateDto.SelectionLevelId).FirstOrDefaultAsync(); | |||||
| if (patternExists is not null) | |||||
| { | |||||
| _logger.LogError($"Pattern already exists in database"); | |||||
| throw new EntityNotFoundException("Pattern already exists in database"); | |||||
| } | |||||
| _logger.LogInformation($"Pattern is not in database"); | |||||
| _logger.LogInformation($"Mapping PatternCreateDto to model"); | |||||
| var pattern = _mapper.Map<Pattern>(patternCreateDto); | |||||
| _logger.LogInformation($"Pattern mapped successfully"); | |||||
| _logger.LogInformation($"Start searching SelectionLevel with id = {patternCreateDto.SelectionLevelId}"); | |||||
| var selectionLevel = await _selectionLevelService.GetByIdEntity(patternCreateDto.SelectionLevelId); | |||||
| if(selectionLevel == null) | |||||
| { | |||||
| _logger.LogError($"SelectionLevel with id = {patternCreateDto.SelectionLevelId} not found"); | |||||
| throw new EntityNotFoundException("Selection level not found"); | |||||
| } | |||||
| _logger.LogInformation($"Add founded SelectionLevel to pattern"); | |||||
| pattern.SelectionLevel = selectionLevel; | |||||
| await _context.AddAsync(pattern); | |||||
| _logger.LogInformation($"Saving Ad to db..."); | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Saved Ad to db..."); | |||||
| await result; | |||||
| } | |||||
| public async Task<ScheduleInterviewResponseDto?> ScheduleIntrviewAsync(ScheduleInterviewDto scheduleInterviewDto) | |||||
| { | |||||
| _logger.LogInformation($"Start scheduling interview"); | |||||
| List<string> NotSentEmails = new(); | |||||
| _logger.LogInformation("Getting pattern from DB by id"); | |||||
| var pattern = await _context.Patterns.Include(x => x.SelectionLevel).ThenInclude(y => y.SelectionProcesses).ThenInclude(z => z.Applicant).Where(x => x.Id == scheduleInterviewDto.PatternId).FirstOrDefaultAsync(); | |||||
| if (pattern == null) | |||||
| { | |||||
| _logger.LogError($"Pattern with id = {scheduleInterviewDto.PatternId} not found"); | |||||
| throw new EntityNotFoundException("Pattern not found"); | |||||
| } | |||||
| _logger.LogInformation("Pattern found in DB"); | |||||
| for (int i = 0; i < scheduleInterviewDto.Emails.Count; i++) | |||||
| { | |||||
| var to = new List<string> { scheduleInterviewDto.Emails[i] }; | |||||
| _logger.LogInformation("Select process where status is \"Čeka na zakazivanje\""); | |||||
| var selectionProcesses = pattern.SelectionLevel.SelectionProcesses.Where(x => x.Status == "Čeka na zakazivanje" && x.Applicant.Email == scheduleInterviewDto.Emails[i]).FirstOrDefault(); | |||||
| if(selectionProcesses != null) | |||||
| { | |||||
| _logger.LogInformation("Selection process is not null"); | |||||
| _logger.LogInformation("Selection process status changing to \"Zakazan\""); | |||||
| await _selectionProcessService.UpdateSelectionProcessStatusAsync(selectionProcesses.Id, new SelectionProcessUpdateStatusDto | |||||
| { | |||||
| Status = "Zakazan" | |||||
| }); | |||||
| _logger.LogInformation("Sending pattern to selected emails on frontend"); | |||||
| await _emailer.SendEmailAsync(to, "Schedule interview", | |||||
| HTMLHelper.SuccessfulStep(pattern.Message, pattern.Title, String.Format("{0:M/d/yyyy}", selectionProcesses.Date)), isHtml: true); | |||||
| } | |||||
| else | |||||
| { | |||||
| _logger.LogInformation("Selection process not found in database"); | |||||
| NotSentEmails.Add(scheduleInterviewDto.Emails[i] + " ne postoji u bazi sa statusom \"Čeka na zakazivanje\" "); | |||||
| continue; | |||||
| } | |||||
| } | |||||
| if(NotSentEmails.Count() > 0) | |||||
| { | |||||
| _logger.LogInformation("List of not set emails are not empty"); | |||||
| _logger.LogInformation("Returning list of not sent emails"); | |||||
| return new ScheduleInterviewResponseDto { NotSentEmails = NotSentEmails }; | |||||
| } | |||||
| _logger.LogInformation("List of not sent email is empty. Return null..."); | |||||
| return null; | |||||
| } | |||||
| public async Task UpdateAsync(PatternUpdateDto patternUpdateDto, int id) | |||||
| { | |||||
| _logger.LogInformation($"Start updating Pattern"); | |||||
| _logger.LogInformation($"Check is Pattern in database"); | |||||
| var patternExists = await _context.Patterns.Include(x => x.SelectionLevel).Where(x => x.Title == patternUpdateDto.Title && x.SelectionLevelId == patternUpdateDto.SelectionLevelId).FirstOrDefaultAsync(); | |||||
| if (patternExists is not null) | |||||
| { | |||||
| _logger.LogError($"Pattern already exists in database"); | |||||
| throw new EntityNotFoundException("Pattern already exists in database"); | |||||
| } | |||||
| _logger.LogInformation($"Start searching Pattern with id = {id}"); | |||||
| var pattern = await _context.Patterns.Where(x => x.Id == id).FirstOrDefaultAsync(); | |||||
| if (pattern is null) | |||||
| { | |||||
| _logger.LogError($"Pattern with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Pattern not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Pattern with id = {id}"); | |||||
| _mapper.Map(patternUpdateDto, pattern); | |||||
| _logger.LogInformation($"Pattern with id = {id} mapped successfully"); | |||||
| _logger.LogInformation($"Start searching SelectionLevel with id = {patternUpdateDto.SelectionLevelId}"); | |||||
| var selectionLevel = await _selectionLevelService.GetByIdEntity(patternUpdateDto.SelectionLevelId); | |||||
| if (selectionLevel == null) | |||||
| { | |||||
| _logger.LogError($"SelectionLevel with id = {patternUpdateDto.SelectionLevelId} not found"); | |||||
| throw new EntityNotFoundException("Selection level not found"); | |||||
| } | |||||
| _logger.LogInformation($"Add founded SelectionLevel to pattern"); | |||||
| pattern.SelectionLevel = selectionLevel; | |||||
| _context.Entry(pattern).State = EntityState.Modified; | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Pattern saved to DB"); | |||||
| await result; | |||||
| } | |||||
| public async Task DeleteAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Pattern with id = {id}"); | |||||
| var pattern = await _context.Patterns.FindAsync(id); | |||||
| if (pattern is null) | |||||
| { | |||||
| _logger.LogError($"Pattern with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Pattern not found"); | |||||
| } | |||||
| _context.Patterns.Remove(pattern); | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Ad saved to DB"); | |||||
| await result; | |||||
| } | |||||
| } | |||||
| } |
| using Bytescout.Spreadsheet; | |||||
| using System.Globalization; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class SaveImportedDataService : ISaveImportedDataService | |||||
| { | |||||
| private readonly ILogger<SaveImportedDataService> _logger; | |||||
| private readonly IAdService _adService; | |||||
| public SaveImportedDataService(ILogger<SaveImportedDataService> logger, IAdService adService) | |||||
| { | |||||
| _logger = logger; | |||||
| _adService = adService; | |||||
| } | |||||
| public async Task<List<ApplicantImportDto>> Save() | |||||
| { | |||||
| List<ApplicantImportDto> applicants = new List<ApplicantImportDto>(); | |||||
| try | |||||
| { | |||||
| _logger.LogInformation("Unoirtubg data from file..."); | |||||
| var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "s.xlsx"); | |||||
| Spreadsheet document = new Spreadsheet(); | |||||
| document.LoadFromFile(path); | |||||
| _logger.LogInformation("File is opened successfully"); | |||||
| var worksheetsNumber = document.Workbook.Worksheets.Count; | |||||
| _logger.LogInformation($"File contains {worksheetsNumber} sheets"); | |||||
| for (int k = 0; k < worksheetsNumber; k++) | |||||
| { | |||||
| var worksheets = document.Workbook.Worksheets[k]; | |||||
| var position = worksheets.Name; | |||||
| _logger.LogInformation($"Import data for Ad {position}"); | |||||
| var ad = await _adService.ImportAsync(new AdCreateDto | |||||
| { | |||||
| Title = position, | |||||
| TechnologiesIds = new(), | |||||
| MinimumExperience = 0, | |||||
| WorkHour = "FullTime", | |||||
| Requirements = "", | |||||
| Offer = "", | |||||
| KeyResponsibilities = "", | |||||
| EmploymentType = position.Contains("praksa") ? "Intership" : "Work", | |||||
| CreatedAt = DateTime.Now, | |||||
| ExpiredAt = DateTime.Now | |||||
| }); | |||||
| int i = 2; | |||||
| while (true) | |||||
| { | |||||
| if (String.IsNullOrEmpty(worksheets.Cell(i, 0).ToString())) | |||||
| break; | |||||
| var name = worksheets.Cell(i, 0).ToString().Split(' '); | |||||
| var a = new ApplicantImportDto | |||||
| { | |||||
| FirstName = name[0], | |||||
| LastName = name[1], | |||||
| Email = worksheets.Cell(i, 1).ToString(), | |||||
| CV = worksheets.Cell(i, 2).ToString(), | |||||
| ApplicationChannel = worksheets.Cell(i, 6).ToString(), | |||||
| Comment = worksheets.Cell(i, 7).ToString(), | |||||
| Position = position, | |||||
| Ad = ad, | |||||
| TypeOfEmployment = position.Contains("praksa") ? "Praksa" : "Posao" | |||||
| }; | |||||
| _logger.LogInformation($"Loaded user {a.FirstName} {a.LastName}"); | |||||
| try | |||||
| { | |||||
| string str = worksheets.Cell(i, 3).ToString(); | |||||
| if(!string.IsNullOrEmpty(str)) | |||||
| a.DateOfApplication = DateTime.ParseExact(str, "dd.MM.yyyy.", CultureInfo.InvariantCulture); | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| _logger.LogError("Incorect date time for this candidate"); | |||||
| } | |||||
| applicants.Add(a); | |||||
| _logger.LogInformation("Candidate added successfully in the list"); | |||||
| i++; | |||||
| } | |||||
| } | |||||
| } | |||||
| catch (Exception e) | |||||
| { | |||||
| _logger.LogError(e.Message); | |||||
| throw new FileNotFoundException("File is not uploaded!"); | |||||
| } | |||||
| return applicants; | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Schedule; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class ScheduleService : IScheduleService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly ILogger<ScheduleService> _logger; | |||||
| public ScheduleService(DatabaseContext context, IMapper mapper,ILogger<ScheduleService> logger) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| _logger = logger; | |||||
| } | |||||
| public async Task<List<ScheduleViewDto>> GetScheduleForCertainPeriod(int month, int year) | |||||
| { | |||||
| _logger.LogInformation("Start getting schedule for certain period"); | |||||
| _logger.LogInformation("Getting data from DB and filter"); | |||||
| var selectionProcessess = await _context.SelectionProcesses | |||||
| .Include(c => c.Applicant) | |||||
| .Include(c => c.SelectionLevel) | |||||
| .Where(k => k.Date != null && k.Date.Value.Month == month && k.Date.Value.Year == year) | |||||
| .ToListAsync(); | |||||
| _logger.LogInformation($"Got {selectionProcessess.Count} selection processes"); | |||||
| _logger.LogInformation($"Return schedule for certain period"); | |||||
| return _mapper.Map<List<ScheduleViewDto>>(selectionProcessess); | |||||
| } | |||||
| } | |||||
| } |
| using Microsoft.AspNetCore.Mvc; | |||||
| using Microsoft.AspNetCore.Mvc.Diagnostics; | |||||
| using Microsoft.Extensions.Caching.Memory; | |||||
| using System.Net; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class ScreeningTestService : IScreeningTestService | |||||
| { | |||||
| private readonly ScreeningTestSettings _settings; | |||||
| private readonly ILogger<ScreeningTestService> _logger; | |||||
| private readonly IMemoryCache _memoryCache; | |||||
| public ScreeningTestService(IOptions<ScreeningTestSettings> settings, ILogger<ScreeningTestService> logger, IMemoryCache memoryCache) | |||||
| { | |||||
| _settings = settings.Value; | |||||
| _logger = logger; | |||||
| _memoryCache = memoryCache; | |||||
| } | |||||
| public async Task<BaseResult<IEnumerable<TestMicroserviceRequest>>> GetScreening() | |||||
| { | |||||
| string token = await GetToken(); | |||||
| _logger.LogInformation($"Start calling microservice to get tests request"); | |||||
| var httpClient = new HttpClient(); | |||||
| var request = new HttpRequestMessage(HttpMethod.Get, _settings.Url + "tests"); | |||||
| request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); | |||||
| _logger.LogInformation("Initilazing http call to microservice"); | |||||
| HttpResponseMessage httpResponseMessage; | |||||
| try | |||||
| { | |||||
| _logger.LogInformation("Calling microservis to get test"); | |||||
| httpResponseMessage = httpClient.SendAsync(request).Result; | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| _logger.LogError($"Error in call: {ex.Message}"); | |||||
| return new BaseResult<IEnumerable<TestMicroserviceRequest>> | |||||
| { | |||||
| IsSuccess = false, | |||||
| DataObject = new List<TestMicroserviceRequest>() | |||||
| }; | |||||
| } | |||||
| if (httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized) | |||||
| { | |||||
| _logger.LogError("Error: Unauthorized"); | |||||
| return new BaseResult<IEnumerable<TestMicroserviceRequest>> | |||||
| { | |||||
| IsSuccess = false, | |||||
| DataObject = new List<TestMicroserviceRequest>() | |||||
| }; | |||||
| } | |||||
| if (httpResponseMessage.StatusCode != HttpStatusCode.OK) | |||||
| { | |||||
| _logger.LogError("Error"); | |||||
| return new BaseResult<IEnumerable<TestMicroserviceRequest>> | |||||
| { | |||||
| IsSuccess = false, | |||||
| DataObject = new List<TestMicroserviceRequest>() | |||||
| }; | |||||
| } | |||||
| var response = httpResponseMessage.Content.ReadAsStringAsync().Result; | |||||
| var resultData = JsonConvert.DeserializeObject<IEnumerable<TestMicroserviceRequest>>(response); | |||||
| _logger.LogInformation($"Call pass and it received: {resultData.Count()} records"); | |||||
| return new BaseResult<IEnumerable<TestMicroserviceRequest>> | |||||
| { | |||||
| DataObject = resultData | |||||
| }; | |||||
| } | |||||
| private async Task<string> GetToken() | |||||
| { | |||||
| string token = ""; | |||||
| if (_memoryCache.TryGetValue("JWT", out string t)) | |||||
| { | |||||
| token = t; | |||||
| } | |||||
| else | |||||
| { | |||||
| var result = await LoginToScreening(); | |||||
| var cacheEntryOptions = new MemoryCacheEntryOptions() | |||||
| .SetSlidingExpiration(TimeSpan.FromSeconds(60)) | |||||
| .SetAbsoluteExpiration(TimeSpan.FromSeconds(3600)) | |||||
| .SetPriority(CacheItemPriority.Normal) | |||||
| .SetSize(1024); | |||||
| _memoryCache.Set("JWT", result.Token, cacheEntryOptions); | |||||
| token = result.Token; | |||||
| } | |||||
| return token; | |||||
| } | |||||
| public async Task<AuthSuccessResponse> LoginToScreening() | |||||
| { | |||||
| _logger.LogInformation($"Start calling microservice to login"); | |||||
| var httpClient = new HttpClient(); | |||||
| var requestUri = new Uri(string.Format(_settings.Url + "auth")); | |||||
| var httpContent = new StringContent(System.Text.Json.JsonSerializer.Serialize(new AuthMicroserviceRequest | |||||
| { | |||||
| Email = _settings.Email, | |||||
| Password = _settings.Password | |||||
| }), | |||||
| Encoding.UTF8, | |||||
| "application/json"); | |||||
| var response = await httpClient.PostAsync(requestUri, httpContent); | |||||
| var content = await response.Content.ReadAsStringAsync(); | |||||
| try | |||||
| { | |||||
| var result = JsonConvert.DeserializeObject<AuthSuccessResponse>(content); | |||||
| var expires = result.Expires.Value - DateTime.Now; | |||||
| var cacheEntryOptions = new MemoryCacheEntryOptions() | |||||
| .SetSlidingExpiration(TimeSpan.FromSeconds(60)) | |||||
| .SetAbsoluteExpiration(expires) | |||||
| .SetPriority(CacheItemPriority.Normal) | |||||
| .SetSize(1024); | |||||
| _memoryCache.Set("JWT", result.Token, cacheEntryOptions); | |||||
| _logger.LogInformation($"Call pass and it received: {result}"); | |||||
| return result; | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| _logger.LogInformation($"Error in call: "); | |||||
| return new AuthSuccessResponse { Token = "" }; | |||||
| } | |||||
| } | |||||
| public async Task<bool> SendTest(TestMicroserviceInviteRequest test) | |||||
| { | |||||
| string token = await GetToken(); | |||||
| _logger.LogInformation($"Start calling microservice to send test request"); | |||||
| var httpClient = new HttpClient(); | |||||
| var request = new HttpRequestMessage(HttpMethod.Post, _settings.Url + "tests"); | |||||
| request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); | |||||
| var httpContent = new StringContent(System.Text.Json.JsonSerializer.Serialize(test), | |||||
| Encoding.UTF8, | |||||
| "application/json"); | |||||
| request.Content = httpContent; | |||||
| _logger.LogInformation("Initilazing http call to microservice"); | |||||
| HttpResponseMessage httpResponseMessage; | |||||
| try | |||||
| { | |||||
| _logger.LogInformation("Calling microservis to send test"); | |||||
| httpResponseMessage = httpClient.SendAsync(request).Result; | |||||
| } | |||||
| catch (Exception ex) | |||||
| { | |||||
| _logger.LogError($"Error in call: {ex.Message}"); | |||||
| return false; | |||||
| } | |||||
| if (httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized) | |||||
| { | |||||
| _logger.LogError("Error: Unauthorized"); | |||||
| return false; | |||||
| } | |||||
| if (httpResponseMessage.StatusCode != HttpStatusCode.OK) | |||||
| { | |||||
| _logger.LogError("Error"); | |||||
| return false; | |||||
| } | |||||
| _logger.LogInformation($"Call pass"); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Stats; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class SelectionLevelService : ISelectionLevelService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly ILogger<SelectionLevelService> _logger; | |||||
| public SelectionLevelService(DatabaseContext context, IMapper mapper, ILogger<SelectionLevelService> logger) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| _logger = logger; | |||||
| } | |||||
| public async Task<List<SelectionLevelResponseWithDataDto>> GetAllAsync() | |||||
| { | |||||
| _logger.LogInformation("Start getting all selection levels"); | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var fromDb = await _context.SelectionLevels | |||||
| .Include(sl => sl.SelectionProcesses) | |||||
| .ThenInclude(sp => sp.Applicant) | |||||
| .Include(sl => sl.SelectionProcesses) | |||||
| .ThenInclude(sp => sp.Scheduler) | |||||
| .ToListAsync(); | |||||
| _logger.LogInformation($"Received {fromDb.Count} levels from db."); | |||||
| _logger.LogInformation($"Mapping received ads to SelectionLevelResponseWithDataDto"); | |||||
| var result = _mapper.Map<List<SelectionLevelResponseWithDataDto>>(fromDb); | |||||
| _logger.LogInformation($"Levels has been mapped and received to client: {result.Count} mapped levels"); | |||||
| return result; | |||||
| } | |||||
| public async Task<SelectionLevelResposneDto> GetByIdAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Level with id = {id}"); | |||||
| var sl = await _context.SelectionLevels.FindAsync(id); | |||||
| if (sl is null) | |||||
| { | |||||
| _logger.LogError($"Level with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Selection level not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Level with id = {id} to SelectionLevelResposneDto"); | |||||
| var result = _mapper.Map<SelectionLevelResposneDto>(sl); | |||||
| _logger.LogInformation($"Level with id = {id} mapped successfully"); | |||||
| return result; | |||||
| } | |||||
| public async Task<SelectionLevel> GetByIdEntity(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Level with id = {id}"); | |||||
| var sl = await _context.SelectionLevels.FindAsync(id); | |||||
| if (sl is null) | |||||
| { | |||||
| _logger.LogError($"Level with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Selection level not found"); | |||||
| } | |||||
| _logger.LogInformation($"Level with id = {id} found and returned to client"); | |||||
| return sl; | |||||
| } | |||||
| public List<SelectionLevelResponseWithDataDto> GetFilteredLevelsAsync(SelectionProcessFilterDto filters) | |||||
| { | |||||
| _logger.LogInformation("Start getting filtered selection levels"); | |||||
| _logger.LogInformation("Getting data from DB and filter it"); | |||||
| var filteredLevels = _context.SelectionLevels | |||||
| .Include(x => x.SelectionProcesses) | |||||
| .ThenInclude(sp => sp.Applicant).ToList() | |||||
| .FilterLevels(filters); | |||||
| _logger.LogInformation($"Received {filteredLevels.Count} levels from db."); | |||||
| _logger.LogInformation($"Mapping received ads to SelectionLevelResponseWithDataDto"); | |||||
| var result = _mapper.Map<List<SelectionLevelResponseWithDataDto>>(filteredLevels); | |||||
| _logger.LogInformation($"Levels has been mapped and received to client: {result.Count} mapped levels"); | |||||
| return result; | |||||
| } | |||||
| public async Task<List<SelectionLevelInfoDto>> GetCountByLevels(List<string> statuses) | |||||
| { | |||||
| _logger.LogInformation("Start getting all Selection levels"); | |||||
| var res = await _context.SelectionLevels.Include(n => n.SelectionProcesses).ToListAsync(); | |||||
| _logger.LogInformation($"Received {res.Count} selection levels"); | |||||
| _logger.LogInformation("Mapping levels with process counts to SelectionLevelInfo"); | |||||
| var resMapped = res.Select(n => new SelectionLevelInfoDto | |||||
| { | |||||
| Level = n.Name, | |||||
| CountAll = n.SelectionProcesses.Count, | |||||
| CountDone = n.SelectionProcesses.Where(n => statuses.Contains(n.Status)).Count() | |||||
| }).ToList(); | |||||
| return resMapped; | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Stats; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class SelectionProcessService : ISelectionProcessService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly UserManager<User> _userManager; | |||||
| private readonly ILogger<SelectionProcessService> _logger; | |||||
| public SelectionProcessService(UserManager<User> userManager,DatabaseContext context, IMapper mapper, ILogger<SelectionProcessService> logger) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| _logger = logger; | |||||
| _userManager = userManager; | |||||
| } | |||||
| public async Task<List<SelectionProcessResposneDto>> GetAllAsync() | |||||
| { | |||||
| _logger.LogInformation("Start getting all Selection Processes"); | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var fromDB = await _context.SelectionProcesses.ToListAsync(); | |||||
| _logger.LogInformation($"Received {fromDB.Count} processes from db."); | |||||
| _logger.LogInformation($"Mapping received ads to SelectionProcessResposneDto"); | |||||
| var result = _mapper.Map<List<SelectionProcessResposneDto>>(fromDB); | |||||
| _logger.LogInformation($"Processes has been mapped and received to client: {result.Count} mapped processes"); | |||||
| return result; | |||||
| } | |||||
| public async Task<bool> FinishSelectionProcess(SelectionProcessCreateDto model) | |||||
| { | |||||
| _logger.LogInformation($"Start finishing selection process with {model.Id}"); | |||||
| var sp = await _context.SelectionProcesses.FindAsync(model.Id); | |||||
| if (sp is null) | |||||
| { | |||||
| _logger.LogError($"Process with id = {model.Id} not found"); | |||||
| throw new EntityNotFoundException("Selection process not found"); | |||||
| } | |||||
| _logger.LogInformation($"Changing status for {model.Id}"); | |||||
| sp.Status = "Odrađen"; | |||||
| _logger.LogInformation($"Skipping throught levels to come to next level"); | |||||
| if (sp.SelectionLevelId == _context.SelectionLevels.OrderBy(n => n.Id).Last().Id) | |||||
| { | |||||
| _logger.LogError($"Applicant is in the last selection level"); | |||||
| throw new EntityNotFoundException("Candidate came to the last selection level"); | |||||
| } | |||||
| var nextLevel = _context.SelectionLevels.AsEnumerable() | |||||
| .SkipWhile(obj => obj.Id != sp.SelectionLevelId) | |||||
| .Skip(1).First(); | |||||
| SelectionProcess newProcess = new SelectionProcess | |||||
| { | |||||
| Name = model.Name, | |||||
| SelectionLevelId = nextLevel.Id, | |||||
| Status = "Čeka na zakazivanje", | |||||
| ApplicantId = sp.ApplicantId | |||||
| }; | |||||
| _context.SelectionProcesses.Add(newProcess); | |||||
| _logger.LogInformation($"Create and add new selection process"); | |||||
| var result = await _context.SaveChangesAsync() > 0; | |||||
| _logger.LogInformation($"Saved changes to db"); | |||||
| return result; | |||||
| } | |||||
| public async Task UpdateSelectionProcessStatusAsync(int id, SelectionProcessUpdateStatusDto selectionProcessUpdateStatusDto) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||||
| var selectionProcess = await _context.SelectionProcesses.FindAsync(id); | |||||
| if (selectionProcess is null) | |||||
| { | |||||
| _logger.LogError($"Selection process with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Selection process not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Selection process with id = {id}"); | |||||
| _mapper.Map(selectionProcessUpdateStatusDto, selectionProcess); | |||||
| _logger.LogInformation($"Selection process with id = {id} mapped successfully"); | |||||
| _context.Entry(selectionProcess).State = EntityState.Modified; | |||||
| var result = _context.SaveChangesAsync(); | |||||
| _logger.LogInformation($"Selection process saved to DB"); | |||||
| await result; | |||||
| } | |||||
| public async Task StatusUpdate(StatusChangeDTO model) | |||||
| { | |||||
| _logger.LogInformation($"Start searching process with id = {model.ProcessId}"); | |||||
| var process = await _context.SelectionProcesses.FindAsync(model.ProcessId); | |||||
| if (process is null) | |||||
| { | |||||
| _logger.LogError($"Process with id = {model.ProcessId} not found"); | |||||
| throw new EntityNotFoundException("Process does not exist."); | |||||
| } | |||||
| _logger.LogInformation($"Check which status is going to be set."); | |||||
| if (model.NewStatus == "Zakazan" && model.SchedulerId is not null) | |||||
| { | |||||
| _logger.LogInformation($"Start searching user with id = {model.SchedulerId}"); | |||||
| var user = await _userManager.FindByIdAsync(model.SchedulerId.ToString()); | |||||
| if (user is null) | |||||
| throw new EntityNotFoundException("Scheduler does not exist."); | |||||
| _logger.LogInformation($"Setting user {user.FirstName} {user.LastName} as scheduler for process with id = {model.ProcessId}"); | |||||
| process.SchedulerId = user.Id; | |||||
| } | |||||
| process.Status = model.NewStatus; | |||||
| process.Date = model.Appointment; | |||||
| process.Comment = model.Comment; | |||||
| _logger.LogInformation($"Processing changes."); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| public async Task InterviewerUpdate(InterviewerUpdateDTO model) | |||||
| { | |||||
| _logger.LogInformation($"Start searching process with id = {model.ProcessId}"); | |||||
| var process = await _context.SelectionProcesses.FindAsync(model.ProcessId); | |||||
| if (process is null) | |||||
| throw new EntityNotFoundException("Process does not exist."); | |||||
| _logger.LogInformation($"Start searching user with id = {model.SchedulerId}"); | |||||
| var user = await _userManager.FindByIdAsync(model.SchedulerId.ToString()); | |||||
| if (user is null) | |||||
| throw new EntityNotFoundException("Scheduler does not exist."); | |||||
| _logger.LogInformation($"Setting user {user.FirstName} {user.LastName} as scheduler for process with id = {model.ProcessId}"); | |||||
| process.SchedulerId = user.Id; | |||||
| _logger.LogInformation("Processing changes..."); | |||||
| await _context.SaveChangesAsync(); | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class TechnologyService : ITechnologyService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly ILogger<TechnologyService> _logger; | |||||
| private readonly IMapper _mapper; | |||||
| public TechnologyService(IMapper mapper, DatabaseContext context, ILogger<TechnologyService> logger) | |||||
| { | |||||
| _mapper = mapper; | |||||
| _context = context; | |||||
| _logger = logger; | |||||
| } | |||||
| public async Task<List<TechnologyResponseDto>> GetAllAsync() | |||||
| { | |||||
| _logger.LogInformation("Start getting all technologies"); | |||||
| var technologies = await _context.Technologies.ToListAsync(); | |||||
| _logger.LogInformation($"Received {technologies.Count} technologies from database"); | |||||
| _logger.LogInformation("Mapping received technologies to TechnologyResponseDto"); | |||||
| var technologiesDto = _mapper.Map<List<TechnologyResponseDto>>(technologies); | |||||
| _logger.LogInformation($"Technologies mapped successfully"); | |||||
| return technologiesDto; | |||||
| } | |||||
| public async Task<TechnologyResponseDto> GetByIdAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Techology with id = {id}"); | |||||
| var technology = await _context.Technologies.FindAsync(id); | |||||
| if (technology is null) | |||||
| { | |||||
| _logger.LogError($"Technology with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Technology not found"); | |||||
| } | |||||
| _logger.LogInformation($"Mapping Technology with id = {id}"); | |||||
| var result = _mapper.Map<TechnologyResponseDto>(technology); | |||||
| _logger.LogInformation($"Technology with id = {id} mapped successfully"); | |||||
| return result; | |||||
| } | |||||
| public async Task<Technology> GetEntityByIdAsync(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching Ad with id = {id}"); | |||||
| var technology = await _context.Technologies.FindAsync(id); | |||||
| if (technology is null) | |||||
| { | |||||
| _logger.LogError($"Technology with id = {id} not found"); | |||||
| throw new EntityNotFoundException("Technology not found"); | |||||
| } | |||||
| _logger.LogInformation($"Technology with id = {id} found successfully"); | |||||
| return technology; | |||||
| } | |||||
| public async Task<List<Technology>> GetEntitiesAsync(int[] technologiesIds) | |||||
| { | |||||
| _logger.LogInformation("Start getting all technologies"); | |||||
| var technologies = await _context.Technologies.Where(x => technologiesIds.Contains(x.TechnologyId)).ToListAsync(); | |||||
| _logger.LogInformation($"Received {technologies.Count} technologies from database"); | |||||
| return technologies; | |||||
| } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| public class UserService : IUserService | |||||
| { | |||||
| private readonly FrontEndSettings _frontEndSettings; | |||||
| private readonly UserManager<User> _userManager; | |||||
| private readonly IMapper _mapper; | |||||
| private readonly DatabaseContext _databaseContext; | |||||
| private readonly IEmailer _emailer; | |||||
| private readonly ILogger<UserService> _logger; | |||||
| public UserService(IOptions<FrontEndSettings> frontEndSettings, UserManager<User> userManager, IMapper mapper, DatabaseContext databaseContext, IEmailer emailer, ILogger<UserService> logger) | |||||
| { | |||||
| _frontEndSettings = frontEndSettings.Value; | |||||
| _userManager = userManager; | |||||
| _mapper = mapper; | |||||
| _databaseContext = databaseContext; | |||||
| _emailer = emailer; | |||||
| _logger = logger; | |||||
| } | |||||
| public async Task<IEnumerable<User?>> GetAll() | |||||
| { | |||||
| _logger.LogInformation("Start getting all users"); | |||||
| _logger.LogInformation("Getting data from DB"); | |||||
| var fromDb = await _userManager.Users.ToListAsync(); | |||||
| _logger.LogInformation($"Received {fromDb.Count} ads from db."); | |||||
| return fromDb; | |||||
| } | |||||
| public async Task<User> GetFirst() | |||||
| { | |||||
| var result = await _userManager.Users.FirstOrDefaultAsync(); | |||||
| if (result == null) | |||||
| throw new EntityNotFoundException("No users in database"); | |||||
| return result; | |||||
| } | |||||
| #region REFACTORING CODE HERE TO CHECK IF USER IS NULL | |||||
| public async Task<User> GetById(int id) | |||||
| { | |||||
| _logger.LogInformation($"Start searching user with id = {id}"); | |||||
| var result = await _userManager.FindByIdAsync(id.ToString()); | |||||
| if (result == null) | |||||
| { | |||||
| throw new EntityNotFoundException("User not found"); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| public async Task<User> GetByEmail(string email) | |||||
| { | |||||
| _logger.LogInformation($"Start searching user with mail = {email}"); | |||||
| var result = await _userManager.FindByEmailAsync(email); | |||||
| if (result == null) | |||||
| { | |||||
| throw new EntityNotFoundException("User not found"); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| #endregion | |||||
| public async Task CreateUser(CreateUserRequestDto model) | |||||
| { | |||||
| _logger.LogInformation($"Start creating user"); | |||||
| var user = _mapper.Map<User>(model); | |||||
| _logger.LogInformation($"User created successfully"); | |||||
| _logger.LogInformation($"Saving user to db..."); | |||||
| await _userManager.CreateAsync(user, model.Password); | |||||
| _logger.LogInformation($"User saved to DB"); | |||||
| } | |||||
| public async Task RemoveUser(User user) | |||||
| { | |||||
| await _userManager.DeleteAsync(user); | |||||
| await _databaseContext.SaveChangesAsync(); | |||||
| } | |||||
| public async Task<bool?> ToggleEnable(User user) | |||||
| { | |||||
| user.IsEnabled = !user.IsEnabled; | |||||
| await _databaseContext.SaveChangesAsync(); | |||||
| return user.IsEnabled; | |||||
| } | |||||
| public async Task<ServiceResponseDTO<object>> SendRegistrationLink(InviteDTO invite) | |||||
| { | |||||
| // check if user exists | |||||
| var check = await _userManager.FindByEmailAsync(invite.Email); | |||||
| if (check != null) | |||||
| return new ServiceResponseDTO<object>() | |||||
| { | |||||
| IsError = true, | |||||
| ErrorMessage = "User already registered." | |||||
| }; | |||||
| // create template user | |||||
| // this user is disabled to log in until confirming invitation | |||||
| var user = new User | |||||
| { | |||||
| UserName = invite.Email, | |||||
| Email = invite.Email, | |||||
| FirstName = invite.FirstName, | |||||
| LastName = invite.LastName, | |||||
| IsEnabled = false | |||||
| }; | |||||
| await _userManager.CreateAsync(user, StringGenerator.GenerateRandomPassword()); | |||||
| // generate invitation token for user | |||||
| // encoded for URLs | |||||
| var token = await _userManager.GeneratePasswordResetTokenAsync(user); | |||||
| token = HttpUtility.UrlEncode(token); | |||||
| // send link | |||||
| await _emailer.SendEmailAndWriteToDbAsync(invite.Email, "Welcome", HTMLHelper.RenderRegisterPage($"{_frontEndSettings.BaseUrl}/register?token={token}&email={invite.Email}"), isHtml: true); | |||||
| await _databaseContext.SaveChangesAsync(); | |||||
| return new ServiceResponseDTO<object> | |||||
| { | |||||
| Data = new { Message = "Link has been sent!" } | |||||
| }; | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class WebhookDefinitionService : IWebhookDefinitionService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| public WebhookDefinitionService(DatabaseContext context, IMapper mapper) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| } | |||||
| public async Task<List<WebhookDefinitionViewDto>> GetAll() | |||||
| { | |||||
| var webhookDefinitions = await _context.WebhookDefinitions.ToListAsync(); | |||||
| return _mapper.Map<List<WebhookDefinitionViewDto>>(webhookDefinitions); | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class WebhookPublisherService : IWebhookPublisherService | |||||
| { | |||||
| private readonly IWebhookSubscriptionService _subscriptionService; | |||||
| public WebhookPublisherService(IWebhookSubscriptionService subscriptionService) | |||||
| { | |||||
| _subscriptionService = subscriptionService; | |||||
| } | |||||
| public async Task PublishAsync(string webhookName, object data) | |||||
| { | |||||
| // Get all subscriptions | |||||
| var subscriptions = await _subscriptionService.GetAllSubscriptionsAsync(webhookName); | |||||
| if (subscriptions == null || subscriptions.Count == 0) | |||||
| return; | |||||
| // Send POST request to subscriptions | |||||
| // 1: define POST request | |||||
| var request = new RestRequest("", Method.Post); | |||||
| // 2: define request body | |||||
| var serializedOrder = JsonConvert.SerializeObject(data, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); | |||||
| request.AddJsonBody(serializedOrder); | |||||
| // 3: define request headers | |||||
| request.AddHeader("Content-Type", "application/json"); | |||||
| foreach (var subscription in subscriptions) | |||||
| { | |||||
| try | |||||
| { | |||||
| // 4: define client endpoint | |||||
| var client = new RestClient(subscription.WebhookURL); | |||||
| // 5: send | |||||
| await client.ExecuteAsync(request); | |||||
| } | |||||
| catch (Exception) | |||||
| { | |||||
| // if one subscription fail continue | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } |
| using System.Diagnostics.CodeAnalysis; | |||||
| namespace Diligent.WebAPI.Business.Services | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class WebhookSubscriptionService : IWebhookSubscriptionService | |||||
| { | |||||
| private readonly DatabaseContext _context; | |||||
| private readonly IMapper _mapper; | |||||
| public WebhookSubscriptionService(DatabaseContext context, IMapper mapper) | |||||
| { | |||||
| _context = context; | |||||
| _mapper = mapper; | |||||
| } | |||||
| public async Task<WebhookSubscription> CreateWebhookSubscription(WebhookSubscriptionCreateDto dto) | |||||
| { | |||||
| // map dto to db model | |||||
| WebhookSubscription subscription = _mapper.Map<WebhookSubscription>(dto); | |||||
| subscription.CreatedAtUtc = DateTime.Now; | |||||
| subscription.IsActive = true; | |||||
| // add to db | |||||
| await _context.WebhookSubscriptions.AddAsync(subscription); | |||||
| await _context.SaveChangesAsync(); | |||||
| return subscription; | |||||
| } | |||||
| public async Task<List<WebhookSubscription>> GetAllSubscriptionsAsync(string webhookName) | |||||
| { | |||||
| return await _context.WebhookSubscriptions | |||||
| .Where(x => x.WebhookDefinition.Name == webhookName) | |||||
| .Include(x => x.WebhookDefinition) | |||||
| .ToListAsync(); | |||||
| } | |||||
| } | |||||
| } |
| using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; | |||||
| namespace Diligent.WebAPI.Business.Settings | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class AuthorizationSettings | |||||
| { | |||||
| public string Secret { get; set; } | |||||
| public int JwtExpiredTime { get; set; } | |||||
| public int JwtRefreshExpiredTime { get; set; } | |||||
| public string GoogleClientId { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Settings | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class FrontEndSettings | |||||
| { | |||||
| public string BaseUrl { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Settings | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class MailSettings | |||||
| { | |||||
| public string SmtpFrom { get; set; } | |||||
| public string SmtpFromName { get; set; } | |||||
| public string SmtpServer { get; set; } | |||||
| public int SmtpPort { get; set; } | |||||
| public bool SmtpUseSSL { get; set; } | |||||
| public string SmtpUsername { get; set; } | |||||
| public string SmtpPassword { get; set; } | |||||
| public string ResetPasswordUrl { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Business.Settings | |||||
| { | |||||
| [ExcludeFromCodeCoverage] | |||||
| public class ScreeningTestSettings | |||||
| { | |||||
| public string Url { get; set; } | |||||
| public string Email { get; set; } | |||||
| public string Password { get; set; } | |||||
| public string Link { get; set; } | |||||
| } | |||||
| } |
| global using Diligent.WebAPI.Business.Services.Interfaces; | |||||
| global using Diligent.WebAPI.Business.Settings; | |||||
| global using Diligent.WebAPI.Business.Helper; | |||||
| global using Diligent.WebAPI.Data; | |||||
| global using Diligent.WebAPI.Data.Entities; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.InsuranceCompany; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.InsurancePolicy; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.Insurer; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.WebhookDefinition; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.WebhookSubscription; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.Ad; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.Auth; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.User; | |||||
| global using Diligent.WebAPI.Contracts.DTOs; | |||||
| global using Diligent.WebAPI.Contracts.Exceptions; | |||||
| global using Diligent.WebAPI.Contracts.Models; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.Technology; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.SelectionLevel; | |||||
| global using Diligent.WebAPI.Contracts.DTOs.Comment; | |||||
| global using Diligent.WebAPI.Business.Extensions; | |||||
| global using Microsoft.EntityFrameworkCore; | |||||
| global using Microsoft.Extensions.Options; | |||||
| global using Microsoft.IdentityModel.Tokens; | |||||
| global using System.IdentityModel.Tokens.Jwt; | |||||
| global using System.Security.Claims; | |||||
| global using System.Text; | |||||
| global using Newtonsoft.Json; | |||||
| global using RestSharp; | |||||
| global using AutoMapper; | |||||
| global using System.Web; | |||||
| global using Microsoft.AspNetCore.Identity; | |||||
| global using Microsoft.Extensions.Logging; | |||||
| global using System.Diagnostics.CodeAnalysis; |
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdApplicantsViewDto | |||||
| { | |||||
| public int Id { get; set; } | |||||
| public string Title { get; set; } | |||||
| public List<AdApplicantViewDto> Applicants { get; set; } | |||||
| public int NubmerOfApplicants { get { return Applicants.Count; } } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdCreateDto | |||||
| { | |||||
| public string Title { get; set; } | |||||
| public int MinimumExperience { get; set; } | |||||
| public DateTime CreatedAt { get; set; } | |||||
| public DateTime ExpiredAt { get; set; } | |||||
| public string KeyResponsibilities { get; set; } | |||||
| public string Requirements { get; set; } | |||||
| public string Offer { get; set; } | |||||
| public string WorkHour { get; set; } | |||||
| public string EmploymentType { get; set; } | |||||
| public List<int> TechnologiesIds { get; set; } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| using Diligent.WebAPI.Contracts.DTOs.Technology; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdDetailsResponseDto | |||||
| { | |||||
| public int Id { get; set; } | |||||
| public string Title { get; set; } | |||||
| public int MinimumExperience { get; set; } | |||||
| public DateTime CreatedAt { get; set; } | |||||
| public DateTime ExpiredAt { get; set; } | |||||
| public string KeyResponsibilities { get; set; } | |||||
| public string Requirements { get; set; } | |||||
| public string Offer { get; set; } | |||||
| public int TotalApplicants { get { return CalculateTotalApplicants(); } } | |||||
| public List<TechnologyResponseDto> Technologies { get; set; } | |||||
| public List<ApplicantViewDto> Applicants { get; set; } | |||||
| private int CalculateTotalApplicants() | |||||
| { | |||||
| return Applicants.Count; | |||||
| } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdFilterDto | |||||
| { | |||||
| public int MinExperience { get; set; } | |||||
| public int MaxExperience { get; set; } | |||||
| public string[]? Technologies { get; set; } | |||||
| public string WorkHour { get; set; } | |||||
| public string EmploymentType { get; set; } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Technology; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdResponseDto | |||||
| { | |||||
| public int Id { get; set; } | |||||
| public string Title { get; set; } | |||||
| public int MinimumExperience { get; set; } | |||||
| public DateTime CreatedAt { get; set; } | |||||
| public DateTime ExpiredAt { get; set; } | |||||
| public string KeyResponsibilities { get; set; } | |||||
| public string Requirements { get; set; } | |||||
| public string Offer { get; set; } | |||||
| public List<TechnologyResponseDto> Technologies { get; set; } | |||||
| public string WorkHour { get; set; } | |||||
| public string EmploymentType { get; set; } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Technology; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdResponseWithCountDto | |||||
| { | |||||
| public int Id { get; set; } | |||||
| public string Title { get; set; } | |||||
| public int MinimumExperience { get; set; } | |||||
| public DateTime CreatedAt { get; set; } | |||||
| public DateTime ExpiredAt { get; set; } | |||||
| public int Count { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdUpdateDto | |||||
| { | |||||
| public string Title { get; set; } | |||||
| public int MinimumExperience { get; set; } | |||||
| public DateTime CreatedAt { get; set; } | |||||
| public DateTime ExpiredAt { get; set; } | |||||
| public string KeyResponsibilities { get; set; } | |||||
| public string Requirements { get; set; } | |||||
| public string Offer { get; set; } | |||||
| public string WorkHour { get; set; } | |||||
| public string EmploymentType { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class SelectionProcessFilterDto | |||||
| { | |||||
| public string[]? Statuses { get; set; } | |||||
| public DateTime? DateStart { get; set; } | |||||
| public DateTime? DateEnd { get; set; } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Technology; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class AdApplicantViewDto | |||||
| { | |||||
| public int ApplicantId { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public DateTime DateOfApplication { get; set; } | |||||
| public string CV { get; set; } | |||||
| public int Experience { get; set; } | |||||
| public List<TechnologyViewDto> TechnologyApplicants { get; set; } = new(); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplicantOptionsDTO | |||||
| { | |||||
| public int ApplicantId { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.Models; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplicantFilterDto : Pagination | |||||
| { | |||||
| public int MinExperience { get; set; } | |||||
| public int MaxExperience { get; set; } | |||||
| public string[]? Technologies { get; set; } | |||||
| public string? EmploymentType { get; set; } | |||||
| public DateTime? MinDateOfApplication { get; set; } | |||||
| public DateTime? MaxDateOfApplication { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplicantImportDto | |||||
| { | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public string Position { get; set; } | |||||
| public string CV { get; set; } | |||||
| public DateTime DateOfApplication { get; set; } | |||||
| public string Email { get; set; } | |||||
| public string PhoneNumber { get; set; } | |||||
| public string LinkedlnLink { get; set; } | |||||
| public string GithubLink { get; set; } | |||||
| public string BitBucketLink { get; set; } | |||||
| public int Experience { get; set; } | |||||
| public string ApplicationChannel { get; set; } | |||||
| public string TypeOfEmployment { get; set; } | |||||
| public string Comment { get; set; } | |||||
| public Diligent.WebAPI.Data.Entities.Ad Ad { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplicantProcessRequestDTO | |||||
| { | |||||
| [Required] | |||||
| public int ApplicantId { get; set; } | |||||
| public int? SchedulerId { get; set; } | |||||
| public DateTime? Appointment { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplicantScheduleViewDto | |||||
| { | |||||
| public int ApplicantId { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Ad; | |||||
| using Diligent.WebAPI.Contracts.DTOs.Comment; | |||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | |||||
| using Diligent.WebAPI.Contracts.DTOs.Technology; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplicantViewDto | |||||
| { | |||||
| public int ApplicantId { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public string Position { get; set; } | |||||
| public DateTime DateOfApplication { get; set; } | |||||
| public string CV { get; set; } | |||||
| public string Email { get; set; } | |||||
| public string PhoneNumber { get; set; } | |||||
| public string LinkedlnLink { get; set; } | |||||
| public string GithubLink { get; set; } | |||||
| public string BitBucketLink { get; set; } | |||||
| public int Experience { get; set; } | |||||
| public string ApplicationChannel { get; set; } | |||||
| public string TypeOfEmployment { get; set; } | |||||
| public string Gender { get; set; } | |||||
| public string ProfessionalQualification { get; set; } | |||||
| public List<TechnologyViewDto> TechnologyApplicants { get; set; } = new(); | |||||
| public List<CommentViewDto> Comments { get; set; } | |||||
| public List<AdResponseDto> Ads { get; set; } | |||||
| public List<SelectionProcessResposneWithoutApplicantDto> SelectionProcesses { get; set; } | |||||
| } | |||||
| } |
| using Microsoft.AspNetCore.Http; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplyForAdRequestDto | |||||
| { | |||||
| public int AdId { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public DateTime DateOfBirth { get; set; } | |||||
| public string PhoneNumber { get; set; } | |||||
| public int[] TechnologiesIds { get; set; } | |||||
| public int Experience { get; set; } | |||||
| public string LinkedinLink { get; set; } | |||||
| public string GithubLink { get; set; } | |||||
| public string BitBucketLink { get; set; } | |||||
| public string Email { get; set; } | |||||
| public string CoverLetter { get; set; } | |||||
| public IFormFile PdfFile { get; set; } | |||||
| public string Gender { get; set; } | |||||
| public string ProfessionalQualification { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class PatternApplicantViewDto | |||||
| { | |||||
| public int ApplicantId { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public string Email { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Auth | |||||
| { | |||||
| public class AuthFailedResponse | |||||
| { | |||||
| public string Error { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Auth | |||||
| { | |||||
| public class AuthenticateRequestDto | |||||
| { | |||||
| [Required] | |||||
| public string Username { get; set; } | |||||
| [Required] | |||||
| public string Password { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Auth | |||||
| { | |||||
| public class AuthenticateResponseDto | |||||
| { | |||||
| public long Id { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public string Username { get; set; } | |||||
| public string Token { get; set; } | |||||
| public string RefreshToken { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Auth | |||||
| { | |||||
| public class CreateUserRequestDto | |||||
| { | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public string UserName { get; set; } | |||||
| public string Email { get; set; } | |||||
| public string Password { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Auth | |||||
| { | |||||
| public class RefreshTokenRequestDto | |||||
| { | |||||
| public string Token { get; set; } | |||||
| public string RefreshToken { get; set; } | |||||
| } | |||||
| } |
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Auth | |||||
| { | |||||
| public class RefreshTokenResultDto | |||||
| { | |||||
| public AuthenticateResponseDto? Data { get; set; } | |||||
| public string? Error { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Comment | |||||
| { | |||||
| public class CommentCreateDto | |||||
| { | |||||
| public string Content { get; set; } | |||||
| public int UserId { get; set; } | |||||
| public int ApplicantId { get; set; } | |||||
| public List<string> UsersToNotify { get; set; } //email | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.User; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Comment | |||||
| { | |||||
| public class CommentViewDto | |||||
| { | |||||
| public string Content { get; set; } | |||||
| public DateTime DateOfSending { get; set; } | |||||
| public UserResponseDTO User { get; set; } | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Error | |||||
| { | |||||
| public class ErrorResponseDto | |||||
| { | |||||
| public string Message { get; set; } | |||||
| public List<ValidationItemDto> ValidationItems { get; set; } = new List<ValidationItemDto>(); | |||||
| public ErrorResponseDto(string message) | |||||
| { | |||||
| Message = message; | |||||
| } | |||||
| public ErrorResponseDto(string message, List<ValidationItemDto> validationItems) | |||||
| { | |||||
| Message = message; | |||||
| ValidationItems = validationItems; | |||||
| } | |||||
| } | |||||
| } |