Преглед изворни кода

Backend initial commit

pull/1/head
bronjaermin пре 3 година
родитељ
комит
3aebe682e5
100 измењених фајлова са 4473 додато и 0 уклоњено
  1. 25
    0
      .dockerignore
  2. 4
    0
      .editorconfig
  3. 21
    0
      .gitignore
  4. 28
    0
      Diligent.WebAPI.Business/Diligent.WebAPI.Business.csproj
  5. 58
    0
      Diligent.WebAPI.Business/Extensions/AdExtensions.cs
  6. 88
    0
      Diligent.WebAPI.Business/Extensions/ApplicantExtensions.cs
  7. 15
    0
      Diligent.WebAPI.Business/Extensions/PaginationExtension.cs
  8. 52
    0
      Diligent.WebAPI.Business/Extensions/PatternExtension.cs
  9. 38
    0
      Diligent.WebAPI.Business/Extensions/SelectionProcessExtensions.cs
  10. 86
    0
      Diligent.WebAPI.Business/Helper/HTMLHelper.cs
  11. 57
    0
      Diligent.WebAPI.Business/Helper/StringGenerator.cs
  12. 25
    0
      Diligent.WebAPI.Business/MappingProfiles/AdMappingProfile.cs
  13. 23
    0
      Diligent.WebAPI.Business/MappingProfiles/ApplicantMappingProfile.cs
  14. 19
    0
      Diligent.WebAPI.Business/MappingProfiles/CommentMappingProfile.cs
  15. 20
    0
      Diligent.WebAPI.Business/MappingProfiles/CompanyMappingProfile.cs
  16. 18
    0
      Diligent.WebAPI.Business/MappingProfiles/InsurerMappingProfile.cs
  17. 25
    0
      Diligent.WebAPI.Business/MappingProfiles/PatternMappingProfile.cs
  18. 13
    0
      Diligent.WebAPI.Business/MappingProfiles/PolicyMappingProfiles.cs
  19. 19
    0
      Diligent.WebAPI.Business/MappingProfiles/SelectionLevelMappingProfile.cs
  20. 23
    0
      Diligent.WebAPI.Business/MappingProfiles/SelectionProcessMappingProfile.cs
  21. 16
    0
      Diligent.WebAPI.Business/MappingProfiles/TechnologyMappingProfile.cs
  22. 29
    0
      Diligent.WebAPI.Business/MappingProfiles/UserMappingProfile.cs
  23. 17
    0
      Diligent.WebAPI.Business/MappingProfiles/WebhookMappingProfile.cs
  24. 215
    0
      Diligent.WebAPI.Business/Services/AdService.cs
  25. 287
    0
      Diligent.WebAPI.Business/Services/ApplicantService.cs
  26. 464
    0
      Diligent.WebAPI.Business/Services/AuthenticationService.cs
  27. 42
    0
      Diligent.WebAPI.Business/Services/CommentService.cs
  28. 389
    0
      Diligent.WebAPI.Business/Services/Emailer.cs
  29. 53
    0
      Diligent.WebAPI.Business/Services/FileService.cs
  30. 53
    0
      Diligent.WebAPI.Business/Services/HttpClientService.cs
  31. 55
    0
      Diligent.WebAPI.Business/Services/ImportService.cs
  32. 64
    0
      Diligent.WebAPI.Business/Services/InsuranceCompaniesService.cs
  33. 76
    0
      Diligent.WebAPI.Business/Services/InsurancePoliciesService.cs
  34. 66
    0
      Diligent.WebAPI.Business/Services/InsurersService.cs
  35. 27
    0
      Diligent.WebAPI.Business/Services/Interfaces/IAdService.cs
  36. 18
    0
      Diligent.WebAPI.Business/Services/Interfaces/IApplicantService.cs
  37. 22
    0
      Diligent.WebAPI.Business/Services/Interfaces/IAuthenticationService.cs
  38. 9
    0
      Diligent.WebAPI.Business/Services/Interfaces/ICommentService.cs
  39. 18
    0
      Diligent.WebAPI.Business/Services/Interfaces/IEmailer.cs
  40. 10
    0
      Diligent.WebAPI.Business/Services/Interfaces/IFileService.cs
  41. 13
    0
      Diligent.WebAPI.Business/Services/Interfaces/IHttpClientService.cs
  42. 9
    0
      Diligent.WebAPI.Business/Services/Interfaces/IImportService.cs
  43. 13
    0
      Diligent.WebAPI.Business/Services/Interfaces/IInsuranceCompaniesService.cs
  44. 15
    0
      Diligent.WebAPI.Business/Services/Interfaces/IInsurancePoliciesService.cs
  45. 14
    0
      Diligent.WebAPI.Business/Services/Interfaces/IInsurersService.cs
  46. 28
    0
      Diligent.WebAPI.Business/Services/Interfaces/IPatternService.cs
  47. 7
    0
      Diligent.WebAPI.Business/Services/Interfaces/ISaveImportedDataService.cs
  48. 9
    0
      Diligent.WebAPI.Business/Services/Interfaces/IScheduleService.cs
  49. 10
    0
      Diligent.WebAPI.Business/Services/Interfaces/IScreeningTestClientService.cs
  50. 9
    0
      Diligent.WebAPI.Business/Services/Interfaces/IScreeningTestService.cs
  51. 16
    0
      Diligent.WebAPI.Business/Services/Interfaces/ISelectionLevelService.cs
  52. 15
    0
      Diligent.WebAPI.Business/Services/Interfaces/ISelectionProcessService.cs
  53. 11
    0
      Diligent.WebAPI.Business/Services/Interfaces/ITechnologyService.cs
  54. 16
    0
      Diligent.WebAPI.Business/Services/Interfaces/IUserService.cs
  55. 10
    0
      Diligent.WebAPI.Business/Services/Interfaces/IWebhookDefinitionService.cs
  56. 7
    0
      Diligent.WebAPI.Business/Services/Interfaces/IWebhookPublisherService.cs
  57. 14
    0
      Diligent.WebAPI.Business/Services/Interfaces/IWebhookSubscriptionService.cs
  58. 241
    0
      Diligent.WebAPI.Business/Services/PatternService.cs
  59. 91
    0
      Diligent.WebAPI.Business/Services/SaveImportedDataService.cs
  60. 33
    0
      Diligent.WebAPI.Business/Services/ScheduleService.cs
  61. 174
    0
      Diligent.WebAPI.Business/Services/ScreeningTestService.cs
  62. 103
    0
      Diligent.WebAPI.Business/Services/SelectionLevelService.cs
  63. 144
    0
      Diligent.WebAPI.Business/Services/SelectionProcessService.cs
  64. 66
    0
      Diligent.WebAPI.Business/Services/TechnologyService.cs
  65. 130
    0
      Diligent.WebAPI.Business/Services/UserService.cs
  66. 23
    0
      Diligent.WebAPI.Business/Services/WebhookDefinitionService.cs
  67. 49
    0
      Diligent.WebAPI.Business/Services/WebhookPublisherService.cs
  68. 39
    0
      Diligent.WebAPI.Business/Services/WebhookSubscriptionService.cs
  69. 13
    0
      Diligent.WebAPI.Business/Settings/AuthorizationSettings.cs
  70. 8
    0
      Diligent.WebAPI.Business/Settings/FrontEndSettings.cs
  71. 15
    0
      Diligent.WebAPI.Business/Settings/MailSettings.cs
  72. 11
    0
      Diligent.WebAPI.Business/Settings/ScreeningTestSettings.cs
  73. 41
    0
      Diligent.WebAPI.Business/Usings.cs
  74. 12
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/AdApplicantsViewDto.cs
  75. 31
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/AdCreateDto.cs
  76. 40
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/AdDetailsResponseDto.cs
  77. 21
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/AdFilterDto.cs
  78. 34
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/AdResponseDto.cs
  79. 18
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/AdResponseWithCountDto.cs
  80. 29
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/AdUpdateDto.cs
  81. 9
    0
      Diligent.WebAPI.Contracts/DTOs/Ad/SelectionProcessFilterDto.cs
  82. 15
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/AdApplicantViewDto.cs
  83. 9
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/AplicantOptionsDTO.cs
  84. 14
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantFilterDto.cs
  85. 21
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantImporteDto.cs
  86. 10
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantProcessRequestDTO.cs
  87. 9
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantScheduleViewDto.cs
  88. 31
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantViewDto.cs
  89. 40
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/ApplyForAdRequestDto.cs
  90. 19
    0
      Diligent.WebAPI.Contracts/DTOs/Applicant/PatternApplicantViewDto.cs
  91. 13
    0
      Diligent.WebAPI.Contracts/DTOs/Auth/AuthFailedResponse.cs
  92. 11
    0
      Diligent.WebAPI.Contracts/DTOs/Auth/AuthenticateRequestDto.cs
  93. 12
    0
      Diligent.WebAPI.Contracts/DTOs/Auth/AuthenticateResponseDto.cs
  94. 21
    0
      Diligent.WebAPI.Contracts/DTOs/Auth/CreateUserRequestDto.cs
  95. 15
    0
      Diligent.WebAPI.Contracts/DTOs/Auth/RefreshTokenRequestDto.cs
  96. 15
    0
      Diligent.WebAPI.Contracts/DTOs/Auth/RefreshTokenResultDto.cs
  97. 10
    0
      Diligent.WebAPI.Contracts/DTOs/Comment/CommentCreateDto.cs
  98. 11
    0
      Diligent.WebAPI.Contracts/DTOs/Comment/CommentViewDto.cs
  99. 19
    0
      Diligent.WebAPI.Contracts/DTOs/Error/ErrorResponseDto.cs
  100. 0
    0
      Diligent.WebAPI.Contracts/DTOs/Error/ValidationItemDto.cs

+ 25
- 0
.dockerignore Прегледај датотеку

@@ -0,0 +1,25 @@
**/.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

+ 4
- 0
.editorconfig Прегледај датотеку

@@ -0,0 +1,4 @@
[*.cs]

# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
dotnet_diagnostic.CS8618.severity = none

+ 21
- 0
.gitignore Прегледај датотеку

@@ -0,0 +1,21 @@
################################################################################
# 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

+ 28
- 0
Diligent.WebAPI.Business/Diligent.WebAPI.Business.csproj Прегледај датотеку

@@ -0,0 +1,28 @@
<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>

+ 58
- 0
Diligent.WebAPI.Business/Extensions/AdExtensions.cs Прегледај датотеку

@@ -0,0 +1,58 @@
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;
}
}
}

+ 88
- 0
Diligent.WebAPI.Business/Extensions/ApplicantExtensions.cs Прегледај датотеку

@@ -0,0 +1,88 @@
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;
}
}
}

+ 15
- 0
Diligent.WebAPI.Business/Extensions/PaginationExtension.cs Прегледај датотеку

@@ -0,0 +1,15 @@
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();
}
}
}

+ 52
- 0
Diligent.WebAPI.Business/Extensions/PatternExtension.cs Прегледај датотеку

@@ -0,0 +1,52 @@
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;
}
}
}

+ 38
- 0
Diligent.WebAPI.Business/Extensions/SelectionProcessExtensions.cs Прегледај датотеку

@@ -0,0 +1,38 @@
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;
}
}
}

+ 86
- 0
Diligent.WebAPI.Business/Helper/HTMLHelper.cs Прегледај датотеку

@@ -0,0 +1,86 @@
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];
//}
}
}

+ 57
- 0
Diligent.WebAPI.Business/Helper/StringGenerator.cs Прегледај датотеку

@@ -0,0 +1,57 @@
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());
}
}
}

+ 25
- 0
Diligent.WebAPI.Business/MappingProfiles/AdMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,25 @@

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
}
}
}

+ 23
- 0
Diligent.WebAPI.Business/MappingProfiles/ApplicantMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,23 @@
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
}
}
}

+ 19
- 0
Diligent.WebAPI.Business/MappingProfiles/CommentMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,19 @@
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
}
}
}

+ 20
- 0
Diligent.WebAPI.Business/MappingProfiles/CompanyMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,20 @@
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
}
}

}

+ 18
- 0
Diligent.WebAPI.Business/MappingProfiles/InsurerMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,18 @@
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
}
}
}

+ 25
- 0
Diligent.WebAPI.Business/MappingProfiles/PatternMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,25 @@
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
}
}
}

+ 13
- 0
Diligent.WebAPI.Business/MappingProfiles/PolicyMappingProfiles.cs Прегледај датотеку

@@ -0,0 +1,13 @@
namespace Diligent.WebAPI.Business.MappingProfiles
{
public class PolicyMappingProfiles : Profile
{
[ExcludeFromCodeCoverage]
public PolicyMappingProfiles()
{
CreateMap<InsurancePolicy, InsurancePolicyViewDto>();
CreateMap<InsurancePolicyCreateDto, InsurancePolicy>();
CreateMap<InsurancePolicyUpdateDto, InsurancePolicy>();
}
}
}

+ 19
- 0
Diligent.WebAPI.Business/MappingProfiles/SelectionLevelMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,19 @@

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
}
}
}

+ 23
- 0
Diligent.WebAPI.Business/MappingProfiles/SelectionProcessMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,23 @@
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
}
}
}

+ 16
- 0
Diligent.WebAPI.Business/MappingProfiles/TechnologyMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,16 @@

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
}
}
}

+ 29
- 0
Diligent.WebAPI.Business/MappingProfiles/UserMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,29 @@
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
}
}
}

+ 17
- 0
Diligent.WebAPI.Business/MappingProfiles/WebhookMappingProfile.cs Прегледај датотеку

@@ -0,0 +1,17 @@
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
}
}
}

+ 215
- 0
Diligent.WebAPI.Business/Services/AdService.cs Прегледај датотеку

@@ -0,0 +1,215 @@
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;
}
}
}

+ 287
- 0
Diligent.WebAPI.Business/Services/ApplicantService.cs Прегледај датотеку

@@ -0,0 +1,287 @@
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
};
}
}
}

+ 464
- 0
Diligent.WebAPI.Business/Services/AuthenticationService.cs Прегледај датотеку

@@ -0,0 +1,464 @@
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()
};
}
}
}

+ 42
- 0
Diligent.WebAPI.Business/Services/CommentService.cs Прегледај датотеку

@@ -0,0 +1,42 @@
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;
}
}
}

+ 389
- 0
Diligent.WebAPI.Business/Services/Emailer.cs Прегледај датотеку

@@ -0,0 +1,389 @@
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);
// }
//}
}
}

+ 53
- 0
Diligent.WebAPI.Business/Services/FileService.cs Прегледај датотеку

@@ -0,0 +1,53 @@
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;
}
}
}

+ 53
- 0
Diligent.WebAPI.Business/Services/HttpClientService.cs Прегледај датотеку

@@ -0,0 +1,53 @@
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;
}
}
}

+ 55
- 0
Diligent.WebAPI.Business/Services/ImportService.cs Прегледај датотеку

@@ -0,0 +1,55 @@
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);
}
}
}
}

+ 64
- 0
Diligent.WebAPI.Business/Services/InsuranceCompaniesService.cs Прегледај датотеку

@@ -0,0 +1,64 @@
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();
}
}
}

+ 76
- 0
Diligent.WebAPI.Business/Services/InsurancePoliciesService.cs Прегледај датотеку

@@ -0,0 +1,76 @@
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();
}
}
}

+ 66
- 0
Diligent.WebAPI.Business/Services/InsurersService.cs Прегледај датотеку

@@ -0,0 +1,66 @@
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();
}
}
}

+ 27
- 0
Diligent.WebAPI.Business/Services/Interfaces/IAdService.cs Прегледај датотеку

@@ -0,0 +1,27 @@

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);
}
}

+ 18
- 0
Diligent.WebAPI.Business/Services/Interfaces/IApplicantService.cs Прегледај датотеку

@@ -0,0 +1,18 @@

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);
}
}

+ 22
- 0
Diligent.WebAPI.Business/Services/Interfaces/IAuthenticationService.cs Прегледај датотеку

@@ -0,0 +1,22 @@
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);
}
}

+ 9
- 0
Diligent.WebAPI.Business/Services/Interfaces/ICommentService.cs Прегледај датотеку

@@ -0,0 +1,9 @@
using Diligent.WebAPI.Contracts.DTOs.Comment;

namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface ICommentService
{
Task CreateComment(CommentCreateDto commentCreateDto);
}
}

+ 18
- 0
Diligent.WebAPI.Business/Services/Interfaces/IEmailer.cs Прегледај датотеку

@@ -0,0 +1,18 @@
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);
}
}

+ 10
- 0
Diligent.WebAPI.Business/Services/Interfaces/IFileService.cs Прегледај датотеку

@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Http;

namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IFileService
{
Task<string> GetCV(string fileName);
Task UploadCV(string fileName,IFormFile file);
}
}

+ 13
- 0
Diligent.WebAPI.Business/Services/Interfaces/IHttpClientService.cs Прегледај датотеку

@@ -0,0 +1,13 @@
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);
}
}

+ 9
- 0
Diligent.WebAPI.Business/Services/Interfaces/IImportService.cs Прегледај датотеку

@@ -0,0 +1,9 @@
using Microsoft.AspNetCore.Http;

namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IImportService
{
Task<List<ApplicantImportDto>> Import(IFormFile fileData);
}
}

+ 13
- 0
Diligent.WebAPI.Business/Services/Interfaces/IInsuranceCompaniesService.cs Прегледај датотеку

@@ -0,0 +1,13 @@
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);
}
}

+ 15
- 0
Diligent.WebAPI.Business/Services/Interfaces/IInsurancePoliciesService.cs Прегледај датотеку

@@ -0,0 +1,15 @@
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);
}
}

+ 14
- 0
Diligent.WebAPI.Business/Services/Interfaces/IInsurersService.cs Прегледај датотеку

@@ -0,0 +1,14 @@
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);
}
}

+ 28
- 0
Diligent.WebAPI.Business/Services/Interfaces/IPatternService.cs Прегледај датотеку

@@ -0,0 +1,28 @@
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);
}
}

+ 7
- 0
Diligent.WebAPI.Business/Services/Interfaces/ISaveImportedDataService.cs Прегледај датотеку

@@ -0,0 +1,7 @@
namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface ISaveImportedDataService
{
Task<List<ApplicantImportDto>> Save();
}
}

+ 9
- 0
Diligent.WebAPI.Business/Services/Interfaces/IScheduleService.cs Прегледај датотеку

@@ -0,0 +1,9 @@
using Diligent.WebAPI.Contracts.DTOs.Schedule;

namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IScheduleService
{
Task<List<ScheduleViewDto>> GetScheduleForCertainPeriod(int month, int year);
}
}

+ 10
- 0
Diligent.WebAPI.Business/Services/Interfaces/IScreeningTestClientService.cs Прегледај датотеку

@@ -0,0 +1,10 @@
namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IScreeningTestClientService
{
Task<AuthSuccessResponse> LoginToScreening(AuthenticateRequestDto model);
Task GetScreening();

}

}

+ 9
- 0
Diligent.WebAPI.Business/Services/Interfaces/IScreeningTestService.cs Прегледај датотеку

@@ -0,0 +1,9 @@
namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IScreeningTestService
{
Task<BaseResult<IEnumerable<TestMicroserviceRequest>>> GetScreening();
Task<bool> SendTest(TestMicroserviceInviteRequest test);
Task<AuthSuccessResponse> LoginToScreening();
}
}

+ 16
- 0
Diligent.WebAPI.Business/Services/Interfaces/ISelectionLevelService.cs Прегледај датотеку

@@ -0,0 +1,16 @@

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);
}
}

+ 15
- 0
Diligent.WebAPI.Business/Services/Interfaces/ISelectionProcessService.cs Прегледај датотеку

@@ -0,0 +1,15 @@

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);
}
}

+ 11
- 0
Diligent.WebAPI.Business/Services/Interfaces/ITechnologyService.cs Прегледај датотеку

@@ -0,0 +1,11 @@

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);
}
}

+ 16
- 0
Diligent.WebAPI.Business/Services/Interfaces/IUserService.cs Прегледај датотеку

@@ -0,0 +1,16 @@
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();
}
}

+ 10
- 0
Diligent.WebAPI.Business/Services/Interfaces/IWebhookDefinitionService.cs Прегледај датотеку

@@ -0,0 +1,10 @@
namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IWebhookDefinitionService
{
/// <summary>
/// Get all webhook definitions
/// </summary>
Task<List<WebhookDefinitionViewDto>> GetAll();
}
}

+ 7
- 0
Diligent.WebAPI.Business/Services/Interfaces/IWebhookPublisherService.cs Прегледај датотеку

@@ -0,0 +1,7 @@
namespace Diligent.WebAPI.Business.Services.Interfaces
{
public interface IWebhookPublisherService
{
Task PublishAsync(string webhookName, object data);
}
}

+ 14
- 0
Diligent.WebAPI.Business/Services/Interfaces/IWebhookSubscriptionService.cs Прегледај датотеку

@@ -0,0 +1,14 @@
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);
}
}

+ 241
- 0
Diligent.WebAPI.Business/Services/PatternService.cs Прегледај датотеку

@@ -0,0 +1,241 @@
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;
}
}
}

+ 91
- 0
Diligent.WebAPI.Business/Services/SaveImportedDataService.cs Прегледај датотеку

@@ -0,0 +1,91 @@
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;
}
}
}

+ 33
- 0
Diligent.WebAPI.Business/Services/ScheduleService.cs Прегледај датотеку

@@ -0,0 +1,33 @@
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);
}
}
}

+ 174
- 0
Diligent.WebAPI.Business/Services/ScreeningTestService.cs Прегледај датотеку

@@ -0,0 +1,174 @@
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;
}
}
}

+ 103
- 0
Diligent.WebAPI.Business/Services/SelectionLevelService.cs Прегледај датотеку

@@ -0,0 +1,103 @@
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;
}
}
}

+ 144
- 0
Diligent.WebAPI.Business/Services/SelectionProcessService.cs Прегледај датотеку

@@ -0,0 +1,144 @@
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();
}
}
}

+ 66
- 0
Diligent.WebAPI.Business/Services/TechnologyService.cs Прегледај датотеку

@@ -0,0 +1,66 @@
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;
}
}
}

+ 130
- 0
Diligent.WebAPI.Business/Services/UserService.cs Прегледај датотеку

@@ -0,0 +1,130 @@
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!" }
};
}
}
}

+ 23
- 0
Diligent.WebAPI.Business/Services/WebhookDefinitionService.cs Прегледај датотеку

@@ -0,0 +1,23 @@
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);
}
}
}

+ 49
- 0
Diligent.WebAPI.Business/Services/WebhookPublisherService.cs Прегледај датотеку

@@ -0,0 +1,49 @@
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
}
}
}
}
}

+ 39
- 0
Diligent.WebAPI.Business/Services/WebhookSubscriptionService.cs Прегледај датотеку

@@ -0,0 +1,39 @@
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();
}
}
}

+ 13
- 0
Diligent.WebAPI.Business/Settings/AuthorizationSettings.cs Прегледај датотеку

@@ -0,0 +1,13 @@
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; }
}
}

+ 8
- 0
Diligent.WebAPI.Business/Settings/FrontEndSettings.cs Прегледај датотеку

@@ -0,0 +1,8 @@
namespace Diligent.WebAPI.Business.Settings
{
[ExcludeFromCodeCoverage]
public class FrontEndSettings
{
public string BaseUrl { get; set; }
}
}

+ 15
- 0
Diligent.WebAPI.Business/Settings/MailSettings.cs Прегледај датотеку

@@ -0,0 +1,15 @@
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; }
}
}

+ 11
- 0
Diligent.WebAPI.Business/Settings/ScreeningTestSettings.cs Прегледај датотеку

@@ -0,0 +1,11 @@
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; }
}
}

+ 41
- 0
Diligent.WebAPI.Business/Usings.cs Прегледај датотеку

@@ -0,0 +1,41 @@
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;

+ 12
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/AdApplicantsViewDto.cs Прегледај датотеку

@@ -0,0 +1,12 @@
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; } }
}
}

+ 31
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/AdCreateDto.cs Прегледај датотеку

@@ -0,0 +1,31 @@
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; }
}
}

+ 40
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/AdDetailsResponseDto.cs Прегледај датотеку

@@ -0,0 +1,40 @@
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;
}
}
}

+ 21
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/AdFilterDto.cs Прегледај датотеку

@@ -0,0 +1,21 @@
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; }
}
}

+ 34
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/AdResponseDto.cs Прегледај датотеку

@@ -0,0 +1,34 @@
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; }
}
}

+ 18
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/AdResponseWithCountDto.cs Прегледај датотеку

@@ -0,0 +1,18 @@
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; }
}
}

+ 29
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/AdUpdateDto.cs Прегледај датотеку

@@ -0,0 +1,29 @@
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; }
}
}

+ 9
- 0
Diligent.WebAPI.Contracts/DTOs/Ad/SelectionProcessFilterDto.cs Прегледај датотеку

@@ -0,0 +1,9 @@
namespace Diligent.WebAPI.Contracts.DTOs.Ad
{
public class SelectionProcessFilterDto
{
public string[]? Statuses { get; set; }
public DateTime? DateStart { get; set; }
public DateTime? DateEnd { get; set; }
}
}

+ 15
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/AdApplicantViewDto.cs Прегледај датотеку

@@ -0,0 +1,15 @@
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();
}
}

+ 9
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/AplicantOptionsDTO.cs Прегледај датотеку

@@ -0,0 +1,9 @@
namespace Diligent.WebAPI.Contracts.DTOs.Applicant
{
public class ApplicantOptionsDTO
{
public int ApplicantId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}

+ 14
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantFilterDto.cs Прегледај датотеку

@@ -0,0 +1,14 @@
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; }
}
}

+ 21
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantImporteDto.cs Прегледај датотеку

@@ -0,0 +1,21 @@
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; }
}
}

+ 10
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantProcessRequestDTO.cs Прегледај датотеку

@@ -0,0 +1,10 @@
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; }
}
}

+ 9
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantScheduleViewDto.cs Прегледај датотеку

@@ -0,0 +1,9 @@
namespace Diligent.WebAPI.Contracts.DTOs.Applicant
{
public class ApplicantScheduleViewDto
{
public int ApplicantId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}

+ 31
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/ApplicantViewDto.cs Прегледај датотеку

@@ -0,0 +1,31 @@
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; }
}
}

+ 40
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/ApplyForAdRequestDto.cs Прегледај датотеку

@@ -0,0 +1,40 @@
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; }
}
}

+ 19
- 0
Diligent.WebAPI.Contracts/DTOs/Applicant/PatternApplicantViewDto.cs Прегледај датотеку

@@ -0,0 +1,19 @@
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; }
}
}

+ 13
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/AuthFailedResponse.cs Прегледај датотеку

@@ -0,0 +1,13 @@
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; }
}
}

+ 11
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/AuthenticateRequestDto.cs Прегледај датотеку

@@ -0,0 +1,11 @@
namespace Diligent.WebAPI.Contracts.DTOs.Auth
{
public class AuthenticateRequestDto
{
[Required]
public string Username { get; set; }

[Required]
public string Password { get; set; }
}
}

+ 12
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/AuthenticateResponseDto.cs Прегледај датотеку

@@ -0,0 +1,12 @@
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; }
}
}

+ 21
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/CreateUserRequestDto.cs Прегледај датотеку

@@ -0,0 +1,21 @@
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; }
}
}

+ 15
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/RefreshTokenRequestDto.cs Прегледај датотеку

@@ -0,0 +1,15 @@
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; }
}
}

+ 15
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/RefreshTokenResultDto.cs Прегледај датотеку

@@ -0,0 +1,15 @@
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; }
}
}

+ 10
- 0
Diligent.WebAPI.Contracts/DTOs/Comment/CommentCreateDto.cs Прегледај датотеку

@@ -0,0 +1,10 @@
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
}
}

+ 11
- 0
Diligent.WebAPI.Contracts/DTOs/Comment/CommentViewDto.cs Прегледај датотеку

@@ -0,0 +1,11 @@
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; }
}
}

+ 19
- 0
Diligent.WebAPI.Contracts/DTOs/Error/ErrorResponseDto.cs Прегледај датотеку

@@ -0,0 +1,19 @@
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;
}
}
}

+ 0
- 0
Diligent.WebAPI.Contracts/DTOs/Error/ValidationItemDto.cs Прегледај датотеку


Неке датотеке нису приказане због велике количине промена

Loading…
Откажи
Сачувај