| using static Diligent.WebAPI.Data.Entities.Applicant; | |||||
| namespace Diligent.WebAPI.Business.Extensions | |||||
| { | |||||
| public static class ApplicantExtensions | |||||
| { | |||||
| public static IQueryable<Applicant> FilterByExperience(this IQueryable<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); | |||||
| } | |||||
| public static IQueryable<Applicant> FilterByEmploymentType(this IQueryable<Applicant> query, string? employmentType) | |||||
| { | |||||
| if (employmentType == null) return query; | |||||
| return query.Where(x => x.TypeOfEmployment == Enum.Parse<TypesOfEmployment>(employmentType)); | |||||
| } | |||||
| public static IQueryable<Applicant> FilterByDateOfApplication(this IQueryable<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); | |||||
| return query.Where(x => x.DateOfApplication >= minDateOfApplication && x.DateOfApplication < maxDateOfApplication); | |||||
| } | |||||
| public static List<Applicant> FilterByTechnologies(this List<Applicant> query, string[]? technologies) | |||||
| { | |||||
| if (technologies is null) | |||||
| { | |||||
| return query; | |||||
| } | |||||
| List<Applicant> filteredApplicants = new(); | |||||
| for (int i = 0; i < query.Count; i++) | |||||
| { | |||||
| for (int j = 0; j < query[i].TechnologyApplicants.Count; j++) | |||||
| { | |||||
| bool s = false; | |||||
| for (int n = 0; n < technologies.Length; n++) | |||||
| { | |||||
| if (query[i].TechnologyApplicants[j].Technology.Name.ToLower() == technologies[n].ToLower()) | |||||
| { | |||||
| s = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (s) | |||||
| { | |||||
| filteredApplicants.Add(query[i]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| return filteredApplicants; | |||||
| } | |||||
| } | |||||
| } |
| using System.Text.Json; | |||||
| using Microsoft.AspNetCore.Http; | |||||
| namespace Diligent.WebAPI.Business.Extensions | |||||
| { | |||||
| public static class HttpExtensions | |||||
| { | |||||
| //extension method for setting header in response | |||||
| public static void AddHeader(this HttpResponse response, string headerName, int number) | |||||
| { | |||||
| var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; //we want to store content inside header of response in CamelCase format | |||||
| response.Headers.Add("Pagination", System.Text.Json.JsonSerializer.Serialize(number, options)); | |||||
| response.Headers.Add("Access-Control-Expose-Headers", headerName); //giving permission for accessing Pagination header to our client | |||||
| } | |||||
| } | |||||
| } |
| #region Model to DTO | #region Model to DTO | ||||
| CreateMap<Ad, AdResponseDto>(); | CreateMap<Ad, AdResponseDto>(); | ||||
| CreateMap<Ad, AdDetailsResponseDto>(); | CreateMap<Ad, AdDetailsResponseDto>(); | ||||
| CreateMap<Ad, AdApplicantsViewDto>(); | |||||
| #endregion | #endregion | ||||
| } | } | ||||
| } | } |
| { | { | ||||
| #region Models to DTOs | #region Models to DTOs | ||||
| CreateMap<Applicant, ApplicantViewDto>(); | CreateMap<Applicant, ApplicantViewDto>(); | ||||
| CreateMap<Applicant, AdApplicantViewDto>(); | |||||
| #endregion | #endregion | ||||
| #region DTOs to Models | #region DTOs to Models |
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| using Diligent.WebAPI.Business.Extensions; | |||||
| using Microsoft.AspNetCore.Http; | |||||
| namespace Diligent.WebAPI.Business.Services | namespace Diligent.WebAPI.Business.Services | ||||
| { | { | ||||
| { | { | ||||
| private readonly DatabaseContext _context; | private readonly DatabaseContext _context; | ||||
| private readonly IMapper _mapper; | private readonly IMapper _mapper; | ||||
| private readonly IHttpContextAccessor _httpContextAccessor; | |||||
| public ApplicantService(DatabaseContext context,IMapper mapper) | |||||
| public ApplicantService(DatabaseContext context, IMapper mapper, IHttpContextAccessor httpContextAccessor) | |||||
| { | { | ||||
| _context = context; | _context = context; | ||||
| _mapper = mapper; | _mapper = mapper; | ||||
| _httpContextAccessor = httpContextAccessor; | |||||
| } | } | ||||
| public async Task<List<ApplicantViewDto>> GetAll() | |||||
| public async Task<List<ApplicantViewDto>> GetFilteredApplicants(ApplicantFilterDto applicantFilterDto) | |||||
| { | { | ||||
| var applicants = await _context.Applicants.Include(c => c.Ads).ToListAsync(); | |||||
| return _mapper.Map<List<ApplicantViewDto>>(applicants); | |||||
| int applicantsToSkip = (applicantFilterDto.CurrentPage - 1) * applicantFilterDto.PageSize; | |||||
| var context = _httpContextAccessor.HttpContext; | |||||
| var allApplicants = _context.Applicants | |||||
| .Include(c => c.Ads) | |||||
| .Include(x => x.TechnologyApplicants) | |||||
| .ThenInclude(x => x.Technology); | |||||
| context.Response.AddHeader("Pagination", allApplicants.ToList().Count); | |||||
| var filteredApplicants = await allApplicants | |||||
| .FilterByExperience(applicantFilterDto.MinExperience, applicantFilterDto.MaxExperience) | |||||
| .FilterByEmploymentType(applicantFilterDto.EmploymentType) | |||||
| .FilterByDateOfApplication(applicantFilterDto.MinDateOfApplication, applicantFilterDto.MaxDateOfApplication) | |||||
| .Skip(applicantsToSkip) | |||||
| .Take(applicantFilterDto.PageSize) | |||||
| .ToListAsync(); | |||||
| filteredApplicants = filteredApplicants.FilterByTechnologies(applicantFilterDto.Technologies); | |||||
| return _mapper.Map<List<ApplicantViewDto>>(filteredApplicants); | |||||
| } | } | ||||
| public async Task<ApplicantViewDto> GetById(int id) | public async Task<ApplicantViewDto> GetById(int id) | ||||
| return _mapper.Map<ApplicantViewDto>(applicant); | return _mapper.Map<ApplicantViewDto>(applicant); | ||||
| } | } | ||||
| public async Task<ApplicantViewDto> GetApplicantWithSelectionProcessesById(int 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) | |||||
| throw new EntityNotFoundException("Applicant not found"); | |||||
| return _mapper.Map<ApplicantViewDto>(applicant); | |||||
| } | |||||
| public async Task CreateApplicant(ApplicantCreateDto applicantCreateDto) | public async Task CreateApplicant(ApplicantCreateDto applicantCreateDto) | ||||
| { | { | ||||
| var applicant = _mapper.Map<Applicant>(applicantCreateDto); | var applicant = _mapper.Map<Applicant>(applicantCreateDto); | ||||
| { | { | ||||
| var applicant = await _context.Applicants.FindAsync(id); | var applicant = await _context.Applicants.FindAsync(id); | ||||
| if(applicant is null) | |||||
| if (applicant is null) | |||||
| throw new EntityNotFoundException("Applicant not found"); | throw new EntityNotFoundException("Applicant not found"); | ||||
| _context.Applicants.Remove(applicant); | _context.Applicants.Remove(applicant); | ||||
| _context.Entry(applicant).State = EntityState.Modified; | _context.Entry(applicant).State = EntityState.Modified; | ||||
| await _context.SaveChangesAsync(); | await _context.SaveChangesAsync(); | ||||
| } | } | ||||
| public async Task<List<AdApplicantsViewDto>> GetAllAdsApplicants() | |||||
| { | |||||
| var adsApplicants = await _context.Ads | |||||
| .Include(a => a.Applicants) | |||||
| .ThenInclude(a => a.TechnologyApplicants).ThenInclude(a => a.Technology).ToListAsync(); | |||||
| return _mapper.Map<List<AdApplicantsViewDto>>(adsApplicants); | |||||
| } | |||||
| public async Task<ApplicantViewDto> GetApplicantWithSelectionProcessesById(int 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) | |||||
| throw new EntityNotFoundException("Applicant not found"); | |||||
| return _mapper.Map<ApplicantViewDto>(applicant); | |||||
| } | |||||
| } | } | ||||
| } | } |
| { | { | ||||
| public interface IApplicantService | public interface IApplicantService | ||||
| { | { | ||||
| Task<List<ApplicantViewDto>> GetAll(); | |||||
| Task<List<ApplicantViewDto>> GetFilteredApplicants(ApplicantFilterDto applicantFilterDto); | |||||
| Task<List<AdApplicantsViewDto>> GetAllAdsApplicants(); | |||||
| Task<ApplicantViewDto> GetById(int id); | Task<ApplicantViewDto> GetById(int id); | ||||
| Task<ApplicantViewDto> GetApplicantWithSelectionProcessesById(int id); | Task<ApplicantViewDto> GetApplicantWithSelectionProcessesById(int id); | ||||
| Task CreateApplicant(ApplicantCreateDto applicantCreateDto); | Task CreateApplicant(ApplicantCreateDto applicantCreateDto); |
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Ad | |||||
| { | |||||
| public class AdApplicantsViewDto | |||||
| { | |||||
| public int Id { get; set; } | |||||
| public string Title { get; set; } | |||||
| public List<AdApplicantViewDto> Applicants { get; set; } | |||||
| public int NubmerOfApplicants { get { return Applicants.Count; } } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Contracts.DTOs.Technology; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class AdApplicantViewDto | |||||
| { | |||||
| public int ApplicantId { get; set; } | |||||
| public string FirstName { get; set; } | |||||
| public string LastName { get; set; } | |||||
| public DateTime DateOfApplication { get; set; } | |||||
| public string CV { get; set; } | |||||
| public int Experience { get; set; } | |||||
| public List<TechnologyViewDto> TechnologyApplicants { get; set; } = new(); | |||||
| } | |||||
| } |
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | |||||
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | { | ||||
| public class ApplicantCreateDto | public class ApplicantCreateDto | ||||
| { | { | ||||
| public string BitBucketLink { get; set; } | public string BitBucketLink { get; set; } | ||||
| public int Experience { get; set; } | public int Experience { get; set; } | ||||
| public string ApplicationChannel { get; set; } | public string ApplicationChannel { get; set; } | ||||
| public string TypeOfEmployment { get; set; } | |||||
| public List<SelectionProcessCreateDto> SelectionProcesses { get; set; } | |||||
| } | } | ||||
| } | } |
| namespace Diligent.WebAPI.Contracts.DTOs.Applicant | |||||
| { | |||||
| public class ApplicantFilterDto | |||||
| { | |||||
| 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; } | |||||
| public int PageSize { get; set; } | |||||
| public int CurrentPage { get; set; } | |||||
| } | |||||
| } |
| public string BitBucketLink { get; set; } | public string BitBucketLink { get; set; } | ||||
| public int Experience { get; set; } | public int Experience { get; set; } | ||||
| public string ApplicationChannel { get; set; } | public string ApplicationChannel { get; set; } | ||||
| public string TypeOfEmployment { get; set; } | |||||
| public List<TechnologyViewDto> TechnologyApplicants { get; set; } = new(); | public List<TechnologyViewDto> TechnologyApplicants { get; set; } = new(); | ||||
| public List<CommentViewDto> Comments { get; set; } | public List<CommentViewDto> Comments { get; set; } | ||||
| public List<AdResponseDto> Ads { get; set; } | public List<AdResponseDto> Ads { get; set; } |
| namespace Diligent.WebAPI.Contracts.Models | |||||
| { | |||||
| public class Pagination | |||||
| { | |||||
| public int CurrentPage { get; set; } | |||||
| public int PageSize { get; set; } | |||||
| } | |||||
| } |
| builder.Property(c => c.GithubLink).IsRequired(false); | builder.Property(c => c.GithubLink).IsRequired(false); | ||||
| builder.Property(c => c.BitBucketLink).IsRequired(false); | builder.Property(c => c.BitBucketLink).IsRequired(false); | ||||
| builder.Property(c => c.ApplicationChannel).IsRequired(false); | builder.Property(c => c.ApplicationChannel).IsRequired(false); | ||||
| builder.Property(c => c.TypeOfEmployment).IsRequired(true).HasConversion<string>(); | |||||
| } | } | ||||
| } | } | ||||
| } | } |
| { | { | ||||
| public class Applicant | public class Applicant | ||||
| { | { | ||||
| public enum TypesOfEmployment | |||||
| { | |||||
| Posao, | |||||
| Intership | |||||
| }; | |||||
| public int ApplicantId { get; set; } | public int ApplicantId { get; set; } | ||||
| public string FirstName { get; set; } | public string FirstName { get; set; } | ||||
| public string LastName { get; set; } | public string LastName { get; set; } | ||||
| public string BitBucketLink { get; set; } | public string BitBucketLink { get; set; } | ||||
| public int Experience { get; set; } | public int Experience { get; set; } | ||||
| public string ApplicationChannel { get; set; } | public string ApplicationChannel { get; set; } | ||||
| public TypesOfEmployment TypeOfEmployment { get; set; } | |||||
| public List<TechnologyApplicant> TechnologyApplicants { get; set; } | public List<TechnologyApplicant> TechnologyApplicants { get; set; } | ||||
| public List<Comment> Comments { get; set; } | public List<Comment> Comments { get; set; } | ||||
| public List<Ad> Ads { get; set; } | public List<Ad> Ads { get; set; } |
| b.HasIndex("ApplicantsApplicantId"); | b.HasIndex("ApplicantsApplicantId"); | ||||
| b.ToTable("AdApplicant"); | |||||
| b.ToTable("AdApplicant", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("AdTechnology", b => | modelBuilder.Entity("AdTechnology", b => | ||||
| b.HasIndex("TechnologiesTechnologyId"); | b.HasIndex("TechnologiesTechnologyId"); | ||||
| b.ToTable("AdTechnology"); | |||||
| b.ToTable("AdTechnology", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Ad", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Ad", b => | ||||
| b.HasKey("Id"); | b.HasKey("Id"); | ||||
| b.ToTable("Ads"); | |||||
| b.ToTable("Ads", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Applicant", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Applicant", b => | ||||
| b.HasKey("ApplicantId"); | b.HasKey("ApplicantId"); | ||||
| b.ToTable("Applicants"); | |||||
| b.ToTable("Applicants", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.AppRole", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.AppRole", b => | ||||
| b.HasIndex("UserId"); | b.HasIndex("UserId"); | ||||
| b.ToTable("Comments"); | |||||
| b.ToTable("Comments", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.InsuranceCompany", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.InsuranceCompany", b => | ||||
| b.HasKey("Id"); | b.HasKey("Id"); | ||||
| b.ToTable("InsuranceCompanies"); | |||||
| b.ToTable("InsuranceCompanies", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.InsurancePolicy", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.InsurancePolicy", b => | ||||
| b.HasIndex("InsurerId"); | b.HasIndex("InsurerId"); | ||||
| b.ToTable("InsurancePolicies"); | |||||
| b.ToTable("InsurancePolicies", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Insurer", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Insurer", b => | ||||
| b.HasIndex("InsuranceCompanyId"); | b.HasIndex("InsuranceCompanyId"); | ||||
| b.ToTable("Insurers"); | |||||
| b.ToTable("Insurers", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.RefreshToken", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.RefreshToken", b => | ||||
| b.HasIndex("UserId"); | b.HasIndex("UserId"); | ||||
| b.ToTable("RefreshTokens"); | |||||
| b.ToTable("RefreshTokens", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.SelectionLevel", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.SelectionLevel", b => | ||||
| b.HasKey("Id"); | b.HasKey("Id"); | ||||
| b.ToTable("SelectionLevels"); | |||||
| b.ToTable("SelectionLevels", (string)null); | |||||
| b.HasData( | b.HasData( | ||||
| new | new | ||||
| b.HasIndex("SelectionLevelId"); | b.HasIndex("SelectionLevelId"); | ||||
| b.ToTable("SelectionProcesses"); | |||||
| b.ToTable("SelectionProcesses", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Technology", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Technology", b => | ||||
| b.HasKey("TechnologyId"); | b.HasKey("TechnologyId"); | ||||
| b.ToTable("Technologies"); | |||||
| b.ToTable("Technologies", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.TechnologyApplicant", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.TechnologyApplicant", b => | ||||
| b.HasIndex("TechnologyId"); | b.HasIndex("TechnologyId"); | ||||
| b.ToTable("ApplicantTechnologies"); | |||||
| b.ToTable("ApplicantTechnologies", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.User", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.User", b => | ||||
| b.HasKey("Id"); | b.HasKey("Id"); | ||||
| b.ToTable("WebhookDefinitions"); | |||||
| b.ToTable("WebhookDefinitions", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Diligent.WebAPI.Data.Entities.WebhookSubscription", b => | modelBuilder.Entity("Diligent.WebAPI.Data.Entities.WebhookSubscription", b => | ||||
| b.HasIndex("WebhookDefinitionId"); | b.HasIndex("WebhookDefinitionId"); | ||||
| b.ToTable("WebhookSubscriptions"); | |||||
| b.ToTable("WebhookSubscriptions", (string)null); | |||||
| }); | }); | ||||
| modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b => | modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b => |
| namespace Diligent.WebAPI.Host.Controllers.V1 | |||||
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | |||||
| namespace Diligent.WebAPI.Host.Controllers.V1 | |||||
| { | { | ||||
| [ApiVersion("1.0")] | [ApiVersion("1.0")] | ||||
| [Route("v{version:apiVersion}/applicants")] | [Route("v{version:apiVersion}/applicants")] | ||||
| [ApiController] | [ApiController] | ||||
| public class ApplicantsController:ControllerBase | |||||
| public class ApplicantsController : ControllerBase | |||||
| { | { | ||||
| private readonly IApplicantService _applicantService; | private readonly IApplicantService _applicantService; | ||||
| _applicantService = applicantService; | _applicantService = applicantService; | ||||
| } | } | ||||
| //[Authorize] | |||||
| [HttpGet] | [HttpGet] | ||||
| public async Task<IActionResult> GetAll() | |||||
| { | |||||
| return Ok(await _applicantService.GetAll()); | |||||
| } | |||||
| public async Task<IActionResult> GetFilteredApplicants([FromQuery] ApplicantFilterDto applicantFilterDto) => | |||||
| Ok(await _applicantService.GetFilteredApplicants(applicantFilterDto)); | |||||
| //[Authorize] | |||||
| [Authorize] | |||||
| [HttpGet("{id}")] | [HttpGet("{id}")] | ||||
| public async Task<IActionResult> GetById(int id) | |||||
| public async Task<IActionResult> GetById(int id) => | |||||
| Ok(await _applicantService.GetById(id)); | |||||
| [Authorize] | |||||
| [HttpGet("adsApplicants")] | |||||
| public async Task<IActionResult> GetAllAdsApplicants() => | |||||
| Ok(await _applicantService.GetAllAdsApplicants()); | |||||
| [Authorize] | |||||
| [HttpDelete] | |||||
| public async Task<IActionResult> DeleteApplicant(int id) | |||||
| { | { | ||||
| return Ok(await _applicantService.GetById(id)); | |||||
| await _applicantService.DeleteApplicant(id); | |||||
| return Ok(); | |||||
| } | } | ||||
| [HttpGet("processes/{id}")] | [HttpGet("processes/{id}")] | ||||
| { | { | ||||
| return Ok(await _applicantService.GetApplicantWithSelectionProcessesById(id)); | return Ok(await _applicantService.GetApplicantWithSelectionProcessesById(id)); | ||||
| } | } | ||||
| } | } | ||||
| } | } |
| builder.ConfigureSwagger(); | builder.ConfigureSwagger(); | ||||
| IServiceCollection services = builder.Services; | IServiceCollection services = builder.Services; | ||||
| services.AddHttpContextAccessor(); | |||||
| services.AddControllers(); | services.AddControllers(); | ||||
| services.AddEndpointsApiExplorer(); | services.AddEndpointsApiExplorer(); | ||||
| } | } |
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetById_ShouldReturn_200OK_WhenApplicantExist() | |||||
| public async Task GetProcesses_ShouldReturn_200OK_WhenApplicantExists() | |||||
| { | { | ||||
| _applicantService.GetById(Arg.Any<int>()).Returns(_applicant); | |||||
| _applicantService.GetApplicantWithSelectionProcessesById(Arg.Any<int>()).Returns(_applicant); | |||||
| ApplicantsController applicantsController = new(_applicantService); | ApplicantsController applicantsController = new(_applicantService); | ||||
| var result = await applicantsController.GetById(1); | |||||
| var result = await applicantsController.GetProcesses(1); | |||||
| (result as OkObjectResult).StatusCode.Should().Be(200); | (result as OkObjectResult).StatusCode.Should().Be(200); | ||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetProcesses_ShouldReturn_200OK_WhenApplicantExists() | |||||
| public async Task GetProcesses_ShouldThrowEntityNotFooundException_WhenApplicantDoesnotExist() | |||||
| { | { | ||||
| _applicantService.GetApplicantWithSelectionProcessesById(Arg.Any<int>()).Returns(_applicant); | |||||
| _applicantService.When(x => x.GetApplicantWithSelectionProcessesById(Arg.Any<int>())).Do(x => { throw new EntityNotFoundException(); }); | |||||
| ApplicantsController applicantsController = new(_applicantService); | ApplicantsController applicantsController = new(_applicantService); | ||||
| var result = await applicantsController.GetProcesses(1); | |||||
| (result as OkObjectResult).StatusCode.Should().Be(200); | |||||
| await Assert.ThrowsAsync<EntityNotFoundException>(() => applicantsController.GetProcesses(1000)); | |||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetById_ShouldThrowEntityNotFooundException_WhenApplicantDontExist() | |||||
| public async Task GetById_ShouldReturn_200OK_WhenApplicantExist() | |||||
| { | { | ||||
| _applicantService.When(x => x.GetById(Arg.Any<int>())).Do(x => { throw new EntityNotFoundException(); }); | |||||
| _applicantService.GetById(Arg.Any<int>()).Returns(_applicant); | |||||
| ApplicantsController applicantsController = new(_applicantService); | ApplicantsController applicantsController = new(_applicantService); | ||||
| await Assert.ThrowsAsync<EntityNotFoundException>(() => applicantsController.GetById(1000)); | |||||
| var result = await applicantsController.GetById(1); | |||||
| (result as OkObjectResult).StatusCode.Should().Be(200); | |||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetProcesses_ShouldThrowEntityNotFooundException_WhenApplicantDoesnotExist() | |||||
| public async Task GetById_ShouldThrowEntityNotFooundException_WhenApplicantDontExist() | |||||
| { | { | ||||
| _applicantService.When(x => x.GetApplicantWithSelectionProcessesById(Arg.Any<int>())).Do(x => { throw new EntityNotFoundException(); }); | |||||
| _applicantService.When(x => x.GetById(Arg.Any<int>())).Do(x => { throw new EntityNotFoundException(); }); | |||||
| ApplicantsController applicantsController = new(_applicantService); | ApplicantsController applicantsController = new(_applicantService); | ||||
| await Assert.ThrowsAsync<EntityNotFoundException>(() => applicantsController.GetProcesses(1000)); | |||||
| await Assert.ThrowsAsync<EntityNotFoundException>(() => applicantsController.GetById(1000)); | |||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetAll_ShouldReturn_200OK_Always() | |||||
| public async Task GetFilteredApplicants_ShouldReturn_200OK_Always() | |||||
| { | { | ||||
| var applicants = new List<ApplicantViewDto> | var applicants = new List<ApplicantViewDto> | ||||
| { | { | ||||
| _applicant | _applicant | ||||
| }; | }; | ||||
| _applicantService.GetAll().Returns(applicants); | |||||
| var filterDto = new ApplicantFilterDto | |||||
| { | |||||
| CurrentPage = 1, | |||||
| PageSize = 4 | |||||
| }; | |||||
| _applicantService.GetFilteredApplicants(filterDto).Returns(applicants); | |||||
| ApplicantsController applicantsController = new(_applicantService); | ApplicantsController applicantsController = new(_applicantService); | ||||
| var result = await applicantsController.GetAll(); | |||||
| var result = await applicantsController.GetFilteredApplicants(filterDto); | |||||
| (result as OkObjectResult).StatusCode.Should().Be(200); | (result as OkObjectResult).StatusCode.Should().Be(200); | ||||
| } | } |
| namespace Diligent.WebAPI.Tests.Controllers | |||||
| { | |||||
| public class CommentsControllerTests | |||||
| { | |||||
| private ICommentService _commentService = Substitute.For<ICommentService>(); | |||||
| public CommentsControllerTests() | |||||
| { | |||||
| } | |||||
| //[Fact] | |||||
| //public async AddComment_ShouldReturn_200_Created_Always() | |||||
| //{ | |||||
| // _commentService.Add | |||||
| //} | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Data.Entities; | |||||
| namespace Diligent.WebAPI.Tests | |||||
| { | |||||
| public static class MockData | |||||
| { | |||||
| public static List<Applicant> GetListOfApplicants() | |||||
| { | |||||
| var applicant = new Applicant | |||||
| { | |||||
| ApplicantId = 1, | |||||
| ApplicationChannel = "Instagram", | |||||
| BitBucketLink = null, | |||||
| CV = "link", | |||||
| DateOfApplication = DateTime.Now, | |||||
| Email = "some@mail.com", | |||||
| Experience = 1, | |||||
| FirstName = "Dzenis", | |||||
| LastName = "Hadzifejzovic", | |||||
| GithubLink = null, | |||||
| LinkedlnLink = null, | |||||
| PhoneNumber = "432424", | |||||
| Position = ".NET Developer", | |||||
| TypeOfEmployment = Applicant.TypesOfEmployment.Intership, | |||||
| SelectionProcesses = new List<SelectionProcess> | |||||
| { | |||||
| new SelectionProcess{ Id = 1, Status = "", Name = ""}, | |||||
| new SelectionProcess{ Id = 2, Status = "", Name = ""}, | |||||
| new SelectionProcess{ Id = 3, Status = "", Name = ""} | |||||
| } | |||||
| }; | |||||
| var applicants = new List<Applicant> | |||||
| { | |||||
| applicant | |||||
| }; | |||||
| return applicants; | |||||
| } | |||||
| public static List<SelectionProcess> GetListOfSelectionProcess() | |||||
| { | |||||
| var selectionProcess = new SelectionProcess | |||||
| { | |||||
| Applicant = GetListOfApplicants()[0], | |||||
| Date = DateTime.Now, | |||||
| Link = "dasda", | |||||
| Name = "adsda", | |||||
| SelectionLevelId = 1, | |||||
| Status = "completed" | |||||
| }; | |||||
| var selectionProcesses = new List<SelectionProcess> | |||||
| { | |||||
| selectionProcess | |||||
| }; | |||||
| return selectionProcesses; | |||||
| ; | |||||
| } | |||||
| public static List<Ad> GetListOfAds() | |||||
| { | |||||
| var ad = new Ad | |||||
| { | |||||
| Applicants = GetListOfApplicants(), | |||||
| CreatedAt = DateTime.Now, | |||||
| ExpiredAt = DateTime.Now.AddDays(5), | |||||
| MinimumExperience = 1, | |||||
| Title = ".NET Intern", | |||||
| KeyResponsibilities = "dasdadas", | |||||
| Offer = "dsadsada", | |||||
| Requirements = "dsadsadas" | |||||
| }; | |||||
| var ads = new List<Ad> | |||||
| { | |||||
| ad | |||||
| }; | |||||
| return ads; | |||||
| } | |||||
| public static List<User> GetListOfUsers() | |||||
| { | |||||
| var user = new User | |||||
| { | |||||
| FirstName = "Dzenis", | |||||
| Email = "dzenis@gmail.com", | |||||
| LastName = "Hadzifejzovic", | |||||
| UserName = "dzenis12" | |||||
| }; | |||||
| var users = new List<User> | |||||
| { | |||||
| user | |||||
| }; | |||||
| return users; | |||||
| } | |||||
| public static List<Comment> GetListOfComments() | |||||
| { | |||||
| var comment = new Comment | |||||
| { | |||||
| Applicant = GetListOfApplicants()[0], | |||||
| Content = "dsadsad", | |||||
| DateOfSending = DateTime.Now, | |||||
| User = GetListOfUsers()[0], | |||||
| }; | |||||
| var comments = new List<Comment> | |||||
| { | |||||
| comment | |||||
| }; | |||||
| return comments; | |||||
| } | |||||
| } | |||||
| } |
| using AutoMapper; | using AutoMapper; | ||||
| using Diligent.WebAPI.Business.Extensions; | |||||
| using Diligent.WebAPI.Business.MappingProfiles; | using Diligent.WebAPI.Business.MappingProfiles; | ||||
| using Diligent.WebAPI.Business.Services; | using Diligent.WebAPI.Business.Services; | ||||
| using Diligent.WebAPI.Contracts.DTOs.Ad; | |||||
| using Diligent.WebAPI.Contracts.DTOs.Applicant; | using Diligent.WebAPI.Contracts.DTOs.Applicant; | ||||
| using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | using Diligent.WebAPI.Contracts.DTOs.SelectionProcess; | ||||
| using Diligent.WebAPI.Contracts.Exceptions; | using Diligent.WebAPI.Contracts.Exceptions; | ||||
| using Diligent.WebAPI.Data.Entities; | using Diligent.WebAPI.Data.Entities; | ||||
| using Microsoft.AspNetCore.Http; | |||||
| using static Diligent.WebAPI.Data.Entities.Applicant; | |||||
| namespace Diligent.WebAPI.Tests.Services | namespace Diligent.WebAPI.Tests.Services | ||||
| { | { | ||||
| public class ApplicantServiceTests | public class ApplicantServiceTests | ||||
| { | { | ||||
| private readonly IMapper _mapper; | private readonly IMapper _mapper; | ||||
| private readonly IHttpContextAccessor _httpContextAccessor = Substitute.For<IHttpContextAccessor>(); | |||||
| private readonly HttpContext _httpContext; | |||||
| private readonly List<Applicant> _applicants; | private readonly List<Applicant> _applicants; | ||||
| private readonly Applicant _applicant; | |||||
| private readonly List<Ad> _ads; | |||||
| private readonly List<SelectionProcess> _selectionProcesses; | |||||
| public ApplicantServiceTests() | public ApplicantServiceTests() | ||||
| { | { | ||||
| _applicant = new Applicant | |||||
| { | |||||
| ApplicantId = 1, | |||||
| ApplicationChannel = "Instagram", | |||||
| BitBucketLink = null, | |||||
| CV = "link", | |||||
| DateOfApplication = DateTime.Now, | |||||
| Email = "some@mail.com", | |||||
| Experience = 1, | |||||
| FirstName = "Dzenis", | |||||
| LastName = "Hadzifejzovic", | |||||
| GithubLink = null, | |||||
| LinkedlnLink = null, | |||||
| PhoneNumber = "432424", | |||||
| Position = ".NET Developer", | |||||
| SelectionProcesses = new List<SelectionProcess> | |||||
| { | |||||
| new SelectionProcess{ Id = 1, Status = "", Name = ""}, | |||||
| new SelectionProcess{ Id = 2, Status = "", Name = ""}, | |||||
| new SelectionProcess{ Id = 3, Status = "", Name = ""} | |||||
| } | |||||
| }; | |||||
| _applicants = new List<Applicant> | |||||
| { | |||||
| _applicant | |||||
| }; | |||||
| _applicants = MockData.GetListOfApplicants(); | |||||
| _ads = MockData.GetListOfAds(); | |||||
| // configure mapper | // configure mapper | ||||
| var configuration = new MapperConfiguration(cfg => cfg.AddProfiles( | var configuration = new MapperConfiguration(cfg => cfg.AddProfiles( | ||||
| new List<Profile> | new List<Profile> | ||||
| { | { | ||||
| new ApplicantMappingProfile(), | new ApplicantMappingProfile(), | ||||
| new SelectionProcessMappingProfile(), | |||||
| new AdMappingProfile(), | |||||
| new SelectionProcessMappingProfile() | |||||
| })); | })); | ||||
| _mapper = new Mapper(configuration); | _mapper = new Mapper(configuration); | ||||
| _httpContext = new DefaultHttpContext(); | |||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetAll_ShouldReturnListOfApplicants_Always() | |||||
| public async Task GetFilteredApplicants_ShouldReturnListOfApplicants_Always() | |||||
| { | { | ||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | ||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| var result = await applicantService.GetAll(); | |||||
| _httpContextAccessor.HttpContext.Returns(_httpContext); | |||||
| result.Should().BeEquivalentTo(_mapper.Map<List<ApplicantViewDto>>(_applicants)); | |||||
| } | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| [Fact] | |||||
| public async Task GetById_ShouldReturnApplicant_WhenApplicantExist() | |||||
| { | |||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| var filterDto = new ApplicantFilterDto | |||||
| { | |||||
| CurrentPage = 1, | |||||
| PageSize = 4 | |||||
| }; | |||||
| var result = await applicantService.GetById(1); | |||||
| var result = await applicantService.GetFilteredApplicants(filterDto); | |||||
| result.Should().BeEquivalentTo(_mapper.Map<ApplicantViewDto>(_applicant)); | |||||
| Assert.Equal(_httpContext.Response.Headers["Pagination"], result.Count.ToString()); | |||||
| result.Should().BeEquivalentTo(_mapper.Map<List<ApplicantViewDto>>(_applicants)); | |||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetApplicantWithSelectionProcessesById_ShouldReturnApplicant_WhenApplicantExists() | |||||
| public async Task GetById_ShouldReturnApplicant_WhenApplicantExist() | |||||
| { | { | ||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | ||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| var result = await applicantService.GetApplicantWithSelectionProcessesById(1); | |||||
| var processes = result.SelectionProcesses; | |||||
| var result = await applicantService.GetById(1); | |||||
| processes.Should().HaveCount(3); | |||||
| result.Should().BeEquivalentTo(_mapper.Map<ApplicantViewDto>(_applicant)); | |||||
| result.Should().BeEquivalentTo(_mapper.Map<ApplicantViewDto>(_applicants[0])); | |||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task GetById_ShouldThrowEntityNotFooundException_WhenApplicantDontExist() | public async Task GetById_ShouldThrowEntityNotFooundException_WhenApplicantDontExist() | ||||
| { | { | ||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | ||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| await Assert.ThrowsAsync<EntityNotFoundException>(async () => await applicantService.GetById(1000)); | await Assert.ThrowsAsync<EntityNotFoundException>(async () => await applicantService.GetById(1000)); | ||||
| } | } | ||||
| public async Task CreateApplicant_ShouldAddEntityIntoDatabase_Always() | public async Task CreateApplicant_ShouldAddEntityIntoDatabase_Always() | ||||
| { | { | ||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | ||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| ApplicantCreateDto applicantCreateDto = new() | ApplicantCreateDto applicantCreateDto = new() | ||||
| { | { | ||||
| GithubLink = null, | GithubLink = null, | ||||
| LinkedlnLink = null, | LinkedlnLink = null, | ||||
| PhoneNumber = "432424", | PhoneNumber = "432424", | ||||
| Position = ".NET Developer" | |||||
| Position = ".NET Developer", | |||||
| TypeOfEmployment = TypesOfEmployment.Intership.ToString(), | |||||
| SelectionProcesses = _mapper.Map<List<SelectionProcessCreateDto>>(_selectionProcesses) | |||||
| }; | }; | ||||
| await applicantService.CreateApplicant(applicantCreateDto); | await applicantService.CreateApplicant(applicantCreateDto); | ||||
| var applicants = await applicantService.GetAll(); | |||||
| var filterDto = new ApplicantFilterDto | |||||
| { | |||||
| CurrentPage = 1, | |||||
| PageSize = 4 | |||||
| }; | |||||
| var result = await applicantService.GetFilteredApplicants(filterDto); | |||||
| Assert.Equal(2, applicants.Count); | |||||
| Assert.Equal(2, result.Count); | |||||
| } | } | ||||
| [Fact] | [Fact] | ||||
| public async Task DeleteApplicant_ShouldDeleteApplicant_WhenApplicantExist() | public async Task DeleteApplicant_ShouldDeleteApplicant_WhenApplicantExist() | ||||
| { | { | ||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | ||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| await applicantService.DeleteApplicant(1); | await applicantService.DeleteApplicant(1); | ||||
| var applicants = await applicantService.GetAll(); | |||||
| var filterDto = new ApplicantFilterDto | |||||
| { | |||||
| CurrentPage = 1, | |||||
| PageSize = 4 | |||||
| }; | |||||
| var applicants = await applicantService.GetFilteredApplicants(filterDto); | |||||
| Assert.Empty(applicants); | Assert.Empty(applicants); | ||||
| } | } | ||||
| public async Task DeleteApplicant_ShouldThrowEntityNotFooundException_WhenApplicantDontExist() | public async Task DeleteApplicant_ShouldThrowEntityNotFooundException_WhenApplicantDontExist() | ||||
| { | { | ||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | ||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| await Assert.ThrowsAsync<EntityNotFoundException>(async () => await applicantService.DeleteApplicant(1000)); | await Assert.ThrowsAsync<EntityNotFoundException>(async () => await applicantService.DeleteApplicant(1000)); | ||||
| } | } | ||||
| public async Task UpdateApplicant_ShouldUpdateApplicant_WhenApplicantExist() | public async Task UpdateApplicant_ShouldUpdateApplicant_WhenApplicantExist() | ||||
| { | { | ||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | ||||
| ApplicantService applicantService = new(databaseContext, _mapper); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| ApplicantUpdateDto applicantUpdateDto = new() | ApplicantUpdateDto applicantUpdateDto = new() | ||||
| { | { | ||||
| Position = "React Developer" | Position = "React Developer" | ||||
| }; | }; | ||||
| await applicantService.UpdateApplicant(1,applicantUpdateDto); | |||||
| await applicantService.UpdateApplicant(1, applicantUpdateDto); | |||||
| var applicant = await applicantService.GetById(1); | var applicant = await applicantService.GetById(1); | ||||
| Assert.Equal(applicant.Position,applicantUpdateDto.Position); | |||||
| Assert.Equal(applicant.Position, applicantUpdateDto.Position); | |||||
| } | } | ||||
| [Fact] | |||||
| public async Task GetAllAdsApplicants_ShouldReturnListOfAdApplicants_Always() | |||||
| { | |||||
| var databaseContext = await Helpers<Ad>.GetDatabaseContext(_ads); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper, _httpContextAccessor); | |||||
| var result = await applicantService.GetAllAdsApplicants(); | |||||
| result.Should().BeEquivalentTo(_mapper.Map<List<AdApplicantsViewDto>>(_ads)); | |||||
| } | |||||
| [Fact] | |||||
| public async Task GetApplicantWithSelectionProcessesById_ShouldReturnApplicant_WhenApplicantExists() | |||||
| { | |||||
| var databaseContext = await Helpers<Applicant>.GetDatabaseContext(_applicants); | |||||
| ApplicantService applicantService = new(databaseContext, _mapper,_httpContextAccessor); | |||||
| var result = await applicantService.GetApplicantWithSelectionProcessesById(1); | |||||
| var processes = result.SelectionProcesses; | |||||
| processes.Should().HaveCount(3); | |||||
| result.Should().BeEquivalentTo(_mapper.Map<ApplicantViewDto>(_applicants[0])); | |||||
| } | |||||
| } | } | ||||
| } | } |
| using AutoMapper; | |||||
| using Diligent.WebAPI.Business.MappingProfiles; | |||||
| using Diligent.WebAPI.Business.Services; | |||||
| using Diligent.WebAPI.Contracts.DTOs.Comment; | |||||
| using Diligent.WebAPI.Data.Entities; | |||||
| using Microsoft.EntityFrameworkCore; | |||||
| namespace Diligent.WebAPI.Tests.Services | |||||
| { | |||||
| public class CommentServiceTests | |||||
| { | |||||
| private readonly IMapper _mapper; | |||||
| private readonly List<Comment> _comments; | |||||
| private readonly List<Applicant> _applicants; | |||||
| private readonly List<User> _users; | |||||
| public CommentServiceTests() | |||||
| { | |||||
| _applicants = MockData.GetListOfApplicants(); | |||||
| _comments = MockData.GetListOfComments(); | |||||
| _users = MockData.GetListOfUsers(); | |||||
| // configure mapper | |||||
| var configuration = new MapperConfiguration(cfg => cfg.AddProfiles( | |||||
| new List<Profile> | |||||
| { | |||||
| new CommentMappingProfile() | |||||
| })); | |||||
| _mapper = new Mapper(configuration); | |||||
| } | |||||
| [Fact] | |||||
| public async Task CreateComment_ShouldUpdatedListOfComments_Always() | |||||
| { | |||||
| var databaseContext = await Helpers<Comment>.GetDatabaseContext(_comments); | |||||
| CommentService applicantService = new(databaseContext, _mapper); | |||||
| var commentCreateDto = new CommentCreateDto | |||||
| { | |||||
| ApplicantId = _applicants[0].ApplicantId, | |||||
| Content = "dsadasd", | |||||
| UserId = _users[0].Id | |||||
| }; | |||||
| await applicantService.CreateComment(commentCreateDto); | |||||
| var comments = await databaseContext.Comments.ToListAsync(); | |||||
| Assert.Equal(2, comments.Count); | |||||
| } | |||||
| } | |||||
| } |