Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,5 @@ public async Task ReturnUsersOnValidLastNameParameters()
Assert.Equal(results.First().LastName, expectedLastName);
}


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using AutoMapper;
using Hng.Application.Features.SuperAdmin.Dto;
using Hng.Application.Features.SuperAdmin.Handlers;
using Hng.Application.Features.SuperAdmin.Queries;
using Hng.Application.Shared.Dtos;
using Hng.Domain.Entities;
using Hng.Infrastructure.Repository.Interface;
using Moq;
using System.Linq.Expressions;
using Xunit;

public class GetUsersMemberOrganizationsByUserIdQueryHandlerShould
{
private readonly Mock<IRepository<User>> _mockRepository;
private readonly IMapper _mapper;
private readonly Mock<IRepository<Organization>> mockOrganizationRepository;
private readonly GetUsersMemberOrganizationsByUserIdQueryHandler handler;

public GetUsersMemberOrganizationsByUserIdQueryHandlerShould()
{
_mockRepository = new Mock<IRepository<User>>();
mockOrganizationRepository = new Mock<IRepository<Organization>>();

// Set up AutoMapper with your profiles
var config = new MapperConfiguration(cfg =>
{
// Add your AutoMapper profiles here
cfg.CreateMap<User, UserSuperDto>();
cfg.CreateMap<Organization, OrganizationDto>();
});

_mapper = config.CreateMapper();
handler = new GetUsersMemberOrganizationsByUserIdQueryHandler(_mockRepository.Object, mockOrganizationRepository.Object, _mapper);
}

[Fact]
public async Task ReturnNullWhenUserIsNotFound()
{
_mockRepository.Setup(repo => repo.GetBySpec(It.IsAny<Expression<Func<User, bool>>>(), It.IsAny<Expression<Func<User, object>>[]>())).ReturnsAsync((User)null);

var result = await handler.Handle(new GetUsersMemberOrganizationsByUserIdQuery(Guid.NewGuid(), new BaseQueryParameters()), CancellationToken.None);

Assert.Null(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using AutoMapper;
using Hng.Application.Features.SuperAdmin.Dto;
using Hng.Application.Features.SuperAdmin.Handlers;
using Hng.Application.Features.SuperAdmin.Queries;
using Hng.Application.Shared.Dtos;
using Hng.Domain.Entities;
using Hng.Infrastructure.Repository.Interface;
using Moq;
using System.Linq.Expressions;
using Xunit;

namespace Hng.Application.Test.Features.SuperAdmin
{
public class GetUsersOwnedOrganizationsByUserIdQueryHandlerShould
{
private readonly Mock<IRepository<User>> _mockRepository;
private readonly IMapper _mapper;
private readonly Mock<IRepository<Domain.Entities.Organization>> mockOrganizationRepository;
private readonly GetUsersOwnedOrganizationsByUserIdQueryHandler handler;

public GetUsersOwnedOrganizationsByUserIdQueryHandlerShould()
{
_mockRepository = new Mock<IRepository<User>>();
mockOrganizationRepository = new Mock<IRepository<Domain.Entities.Organization>>();

var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserSuperDto>();
cfg.CreateMap<Domain.Entities.Organization, OrganizationDto>();
});
_mapper = config.CreateMapper();

handler = new GetUsersOwnedOrganizationsByUserIdQueryHandler(_mockRepository.Object, mockOrganizationRepository.Object, _mapper);
}

[Fact]
public async Task ReturnNullWhenUserIsNotFound()
{
_mockRepository.Setup(repo => repo.GetBySpec(It.IsAny<Expression<Func<User, bool>>>(), It.IsAny<Expression<Func<User, object>>[]>())).ReturnsAsync((User)null);

var result = await handler.Handle(new GetUsersOwnedOrganizationsByUserIdQuery(Guid.NewGuid(), new BaseQueryParameters()), CancellationToken.None);

Assert.Null(result);
}
}
}
31 changes: 31 additions & 0 deletions src/Hng.Application/Features/SuperAdmin/Dto/OrganizationDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Text.Json.Serialization;

namespace Hng.Application.Features.SuperAdmin.Dto;

public class OrganizationDto
{

[JsonPropertyName("name")] public string Name { get; set; }

[JsonPropertyName("description")] public string Description { get; set; }

[JsonPropertyName("slug")] public string Slug { get; set; }

[JsonPropertyName("email")] public string Email { get; set; }

[JsonPropertyName("industry")] public string Industry { get; set; }

[JsonPropertyName("type")] public string Type { get; set; }

[JsonPropertyName("country")] public string Country { get; set; }

[JsonPropertyName("address")] public string Address { get; set; }

[JsonPropertyName("state")] public string State { get; set; }

[JsonPropertyName("is_active")] public bool IsActive { get; set; }

[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }

}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field 'GetUsersMemberOrganizationsByUserIdQueryHandler._authenticationService' is never used

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using AutoMapper;
using Hng.Application.Features.SuperAdmin.Dto;
using Hng.Application.Features.SuperAdmin.Queries;
using Hng.Application.Shared.Dtos;
using Hng.Domain.Entities;
using Hng.Infrastructure.Repository.Interface;
using Hng.Infrastructure.Services.Interfaces;
using MediatR;

namespace Hng.Application.Features.SuperAdmin.Handlers
{
public class GetUsersMemberOrganizationsByUserIdQueryHandler : IRequestHandler<GetUsersMemberOrganizationsByUserIdQuery, PagedListDto<OrganizationDto>>
{
private readonly IRepository<User> _userRepository;
private readonly IRepository<Organization> _organizationRepository;
private readonly IMapper _mapper;
private readonly IAuthenticationService _authenticationService;

Check warning on line 17 in src/Hng.Application/Features/SuperAdmin/Handlers/GetUsersMemberOrganizationsByUserIdQueryHandler.cs

View workflow job for this annotation

GitHub Actions / build_lint_test

The field 'GetUsersMemberOrganizationsByUserIdQueryHandler._authenticationService' is never used

public GetUsersMemberOrganizationsByUserIdQueryHandler(IRepository<User> userRepository,
IRepository<Organization> organizationRepository, IMapper mapper)
{
_userRepository = userRepository;
_organizationRepository = organizationRepository;
_mapper = mapper;
}

public async Task<PagedListDto<OrganizationDto>> Handle(GetUsersMemberOrganizationsByUserIdQuery request, CancellationToken cancellationToken)
{
var user = await _userRepository.GetBySpec(user => user.Id == request.UserId, user => user.Organizations);

if (user == null)
{
return null;
}

var userMemberOrganizations = user.Organizations.Where(org => org.OwnerId != user.Id).ToList();

var mappedOrganizations = _mapper.Map<List<OrganizationDto>>(userMemberOrganizations);

var organizationResult = PagedListDto<OrganizationDto>.ToPagedList(mappedOrganizations, request.UserMemberOrganizationsQueryParameter.Offset, request.UserMemberOrganizationsQueryParameter.Limit);

return organizationResult;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user is not found, you can return an empty list instead of the null.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using AutoMapper;
using Hng.Application.Features.SuperAdmin.Dto;
using Hng.Application.Features.SuperAdmin.Queries;
using Hng.Application.Shared.Dtos;
using Hng.Domain.Entities;
using Hng.Infrastructure.Repository.Interface;
using Hng.Infrastructure.Services.Interfaces;
using MediatR;

namespace Hng.Application.Features.SuperAdmin.Handlers
{
public class GetUsersOwnedOrganizationsByUserIdQueryHandler : IRequestHandler<GetUsersOwnedOrganizationsByUserIdQuery, PagedListDto<OrganizationDto>>
{
private readonly IRepository<User> _userRepository;
private readonly IRepository<Organization> _organizationRepository;
private readonly IMapper _mapper;
private readonly IAuthenticationService _authenticationService;

Check warning on line 17 in src/Hng.Application/Features/SuperAdmin/Handlers/GetUsersOwnedOrganizationsByUserIdQueryHandler.cs

View workflow job for this annotation

GitHub Actions / build_lint_test

The field 'GetUsersOwnedOrganizationsByUserIdQueryHandler._authenticationService' is never used

public GetUsersOwnedOrganizationsByUserIdQueryHandler(IRepository<User> userRepository,
IRepository<Organization> organizationRepository, IMapper mapper)
{
_userRepository = userRepository;
_organizationRepository = organizationRepository;
_mapper = mapper;
}

public async Task<PagedListDto<OrganizationDto>> Handle(GetUsersOwnedOrganizationsByUserIdQuery request, CancellationToken cancellationToken)
{
var user = await _userRepository.GetBySpec(user => user.Id == request.UserId, user => user.Organizations);

if (user == null)
{
return null;
}

var userOwnedOrganizations = user.Organizations.Where(org => org.OwnerId == user.Id).ToList();

var mappedOrganizations = _mapper.Map<List<OrganizationDto>>(userOwnedOrganizations);

var organizationResult = PagedListDto<OrganizationDto>.ToPagedList(mappedOrganizations, request.UserOwnedOrganizationsQueryParameter.Offset, request.UserOwnedOrganizationsQueryParameter.Limit);

return organizationResult;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class AdminUsersMappingProfile : AutoMapper.Profile
public AdminUsersMappingProfile()
{
CreateMap<User, UserSuperDto>().ReverseMap();
CreateMap<Organization, OrganizationDto>().ReverseMap();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Hng.Application.Features.SuperAdmin.Dto;
using Hng.Application.Shared.Dtos;
using MediatR;

namespace Hng.Application.Features.SuperAdmin.Queries
{
public class GetUsersMemberOrganizationsByUserIdQuery : IRequest<PagedListDto<OrganizationDto>>
{
public GetUsersMemberOrganizationsByUserIdQuery(Guid userId, BaseQueryParameters userMemberOrganizationsQueryParameter)
{
UserId = userId;
UserMemberOrganizationsQueryParameter = userMemberOrganizationsQueryParameter;
}

public Guid UserId { get; set; }
public BaseQueryParameters UserMemberOrganizationsQueryParameter { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Hng.Application.Features.SuperAdmin.Dto;
using Hng.Application.Shared.Dtos;
using MediatR;

namespace Hng.Application.Features.SuperAdmin.Queries;

public class GetUsersOwnedOrganizationsByUserIdQuery : IRequest<PagedListDto<OrganizationDto>>
{
public GetUsersOwnedOrganizationsByUserIdQuery(Guid userId, BaseQueryParameters userOwnedOrganizationsQueryParameter)
{
UserId = userId;
UserOwnedOrganizationsQueryParameter = userOwnedOrganizationsQueryParameter;
}
public Guid UserId { get; set; }
public BaseQueryParameters UserOwnedOrganizationsQueryParameter { get; set; }
}
1 change: 0 additions & 1 deletion src/Hng.Infrastructure/Repository/Interface/IRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public interface IRepository<T> where T : EntityBase
Task<T> GetBySpec(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);
Task<IEnumerable<T>> GetAllAsync(params Expression<Func<T, object>>[] includeProperties);
Task<IEnumerable<T>> GetAllBySpec(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties);

IQueryable<T> GetQueryableBySpec(Expression<Func<T, bool>> predicate);
Task<T> AddAsync(T entity);
Task AddRangeAsync(IEnumerable<T> entities);
Expand Down
13 changes: 6 additions & 7 deletions src/Hng.Infrastructure/Repository/Repository.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Linq.Expressions;
using Hng.Domain.Entities;
using Hng.Infrastructure.Context;
using Hng.Infrastructure.Repository.Interface;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;

namespace Hng.Infrastructure.Repository
{
Expand Down Expand Up @@ -85,11 +85,11 @@ public async Task<IEnumerable<T>> GetAllBySpec(Expression<Func<T, bool>> predica
return await entities.ToListAsync();
}


public IQueryable<T> GetQueryableBySpec(Expression<Func<T, bool>> predicate)
{
return _context.Set<T>().AsNoTracking().Where(predicate);
}

public async Task<T> GetBySpec(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
{
var entities = _context.Set<T>().Where(predicate).AsNoTracking();
Expand All @@ -109,12 +109,11 @@ public Task UpdateAsync(T entity)

public async Task SaveChanges()
{

//Add newly registered user to the organisation if they were invited
// Add newly registered user to the organisation if they were invited
var newUsers = _context.ChangeTracker.Entries<User>()
.Where(e => e.State == EntityState.Added)
.Select(e => e.Entity)
.ToList();
.Where(e => e.State == EntityState.Added)
.Select(e => e.Entity)
.ToList();

if (newUsers.Count != 0)
{
Expand Down
60 changes: 60 additions & 0 deletions src/Hng.Web/Controllers/AdminController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,65 @@ public async Task<ActionResult> GetUsersBySearch([FromQuery] UsersQueryParameter
var users = await _mediator.Send(new GetUsersBySearchQuery(parameters));
return Ok(new PaginatedResponseDto<PagedListDto<UserSuperDto>> { Data = users, Metadata = users.MetaData });
}

/// <summary>
/// Admin: Users - gets all organizations owned by a user by the user's id
/// </summary>
/// <returns></returns>
[HttpGet("users/{id}/organizations/owned")]
[Authorize]
[ProducesResponseType(typeof(SuccessResponseDto<PaginatedResponseDto<PagedListDto<OrganizationDto>>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(FailureResponseDto<object>), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(StatusCodeResponse), StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetUserOwnedOrganizationsByUserId([FromRoute] Guid id, [FromQuery] BaseQueryParameters parameters)
{
if (!Guid.TryParse(id.ToString(), out _))
{
return BadRequest(new FailureResponseDto<object> { Message = "Valid user ID must be Provided" });
}

var userOrganizations = await _mediator.Send(new GetUsersOwnedOrganizationsByUserIdQuery(id, parameters));

if (userOrganizations == null)
{
return NotFound(new StatusCodeResponse
{
Message = "User not found",
StatusCode = StatusCodes.Status404NotFound
});
}

return Ok(new PaginatedResponseDto<PagedListDto<OrganizationDto>> { Data = userOrganizations, Metadata = userOrganizations.MetaData });
}

/// <summary>
/// Admin: Users - gets all organizations a user belongs by the user's id
/// </summary>
/// <returns></returns>
[HttpGet("users/{id}/organizations/member")]
[Authorize]
[ProducesResponseType(typeof(SuccessResponseDto<PaginatedResponseDto<PagedListDto<OrganizationDto>>>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(FailureResponseDto<object>), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(StatusCodeResponse), StatusCodes.Status404NotFound)]
public async Task<ActionResult> GetUserMemberOrganizationsByUserId([FromRoute] Guid id, [FromQuery] BaseQueryParameters parameters)
{
if (!Guid.TryParse(id.ToString(), out _))
{
return BadRequest(new FailureResponseDto<object> { Message = "Valid user ID must be Provided" });
}

var userOrganizations = await _mediator.Send(new GetUsersMemberOrganizationsByUserIdQuery(id, parameters));

if (userOrganizations == null)
{
return NotFound(new StatusCodeResponse
{
Message = "User not found",
StatusCode = StatusCodes.Status404NotFound
});
}

return Ok(new PaginatedResponseDto<PagedListDto<OrganizationDto>> { Data = userOrganizations, Metadata = userOrganizations.MetaData });
}
}
}
Loading