Initial code commit.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
using AutoMapper;
|
||||
using FluentValidation;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
|
||||
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
|
||||
using Sufi.Demo.PeopleDirectory.Shared.Wrapper;
|
||||
|
||||
namespace Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Commands
|
||||
{
|
||||
public class AddEditContactCommand : IRequest<IResult<int>>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string UserName { get; set; } = null!;
|
||||
public string Phone { get; set; } = Random.Shared.Next(1000000000, 1999999999).ToString();
|
||||
public string Email { get; set; } = "user@example.com";
|
||||
public string SkillSets { get; set; } = "skill1, skill2, skill3";
|
||||
public string Hobby { get; set; } = "Hobby";
|
||||
}
|
||||
|
||||
public sealed class AddEditContactCommandValidator : AbstractValidator<AddEditContactCommand>
|
||||
{
|
||||
public AddEditContactCommandValidator()
|
||||
{
|
||||
RuleFor(v => v.UserName)
|
||||
.NotEmpty().WithMessage("UserName is required.")
|
||||
.MaximumLength(50).WithMessage("UserName must not exceed 50 characters.");
|
||||
RuleFor(v => v.Phone)
|
||||
.NotEmpty().WithMessage("Phone is required.")
|
||||
.MaximumLength(20).WithMessage("Phone must not exceed 20 characters.");
|
||||
RuleFor(v => v.Email)
|
||||
.NotEmpty().WithMessage("Email is required.")
|
||||
.EmailAddress().WithMessage("A valid email is required.")
|
||||
.MaximumLength(100)
|
||||
.WithMessage("Email must not exceed 100 characters.");
|
||||
RuleFor(v => v.SkillSets)
|
||||
.NotEmpty().WithMessage("SkillSets is required.")
|
||||
.MaximumLength(255).WithMessage("SkillSets must not exceed 255 characters.");
|
||||
RuleFor(v => v.Hobby)
|
||||
.NotEmpty().WithMessage("Hobby is required.")
|
||||
.MaximumLength(255).WithMessage("Hobby must not exceed 255 characters.");
|
||||
}
|
||||
}
|
||||
|
||||
public class AddEditContactCommandHandler(
|
||||
IMapper mapper,
|
||||
IUnitOfWork<int> unitOfWork,
|
||||
ILogger<AddEditContactCommandHandler> logger,
|
||||
IAppCache appCache
|
||||
) : IRequestHandler<AddEditContactCommand, IResult<int>>
|
||||
{
|
||||
public async Task<IResult<int>> Handle(AddEditContactCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
if (command.Id == 0)
|
||||
{
|
||||
// Only add if max count is not more than 100.
|
||||
var count = await unitOfWork.Repository<Contact>().CountAsync();
|
||||
if (count > 100)
|
||||
{
|
||||
return await Result<int>.FailAsync("Max item count reached. Please delete some first.");
|
||||
}
|
||||
|
||||
var contact = mapper.Map<Contact>(command);
|
||||
await unitOfWork.Repository<Contact>().AddAsync(contact);
|
||||
await unitOfWork.Commit(cancellationToken);
|
||||
|
||||
// Invalidate cache.
|
||||
await appCache.RemoveAsync("contact_all");
|
||||
|
||||
logger.LogInformation("New contact added with ID: {Id}", contact.Id);
|
||||
|
||||
return await Result<int>.SuccessAsync(contact.Id, "New contact saved.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var contact = await unitOfWork.Repository<Contact>().GetByIdAsync(command.Id);
|
||||
if (contact != null)
|
||||
{
|
||||
mapper.Map(command, contact);
|
||||
|
||||
await unitOfWork.Repository<Contact>().UpdateAsync(contact);
|
||||
await unitOfWork.Commit(cancellationToken);
|
||||
|
||||
// Invalidate cache.
|
||||
await appCache.RemoveAsync($"contact_{command.Id}");
|
||||
await appCache.RemoveAsync("contact_all");
|
||||
|
||||
logger.LogInformation("Contact updated with ID: {Id}", contact.Id);
|
||||
|
||||
return await Result<int>.SuccessAsync(contact.Id, "Contact updated.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Contact not found with ID: {Id}", command.Id);
|
||||
|
||||
return await Result<int>.FailAsync("Contact not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using FluentValidation;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
|
||||
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
|
||||
using Sufi.Demo.PeopleDirectory.Shared.Wrapper;
|
||||
|
||||
namespace Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Commands
|
||||
{
|
||||
public class DeleteContactCommand : IRequest<IResult>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public sealed class DeleteContactCommandValidator : AbstractValidator<DeleteContactCommand>
|
||||
{
|
||||
public DeleteContactCommandValidator()
|
||||
{
|
||||
RuleFor(v => v.Id)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("A valid Id is required.");
|
||||
}
|
||||
}
|
||||
|
||||
public class DeleteContactCommandHandler(
|
||||
IUnitOfWork<int> unitOfWork,
|
||||
ILogger<DeleteContactCommandHandler> logger,
|
||||
IAppCache appCache
|
||||
) : IRequestHandler<DeleteContactCommand, IResult>
|
||||
{
|
||||
public async Task<IResult> Handle(DeleteContactCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var itemToDelete = await unitOfWork.Repository<Contact>().GetByIdAsync(request.Id);
|
||||
if (itemToDelete != null)
|
||||
{
|
||||
await unitOfWork.Repository<Contact>().DeleteByIdAsync(request.Id);
|
||||
|
||||
// Clear cache entries related to contacts.
|
||||
await appCache.RemoveAsync($"contact_{request.Id}");
|
||||
await appCache.RemoveAsync("contact_all");
|
||||
|
||||
logger.LogInformation("Contact with ID: {Id} deleted.", request.Id);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
logger.LogWarning("No contact found with ID: {Id}", request.Id);
|
||||
|
||||
return Result.Fail("No data to delete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using AutoMapper;
|
||||
using MediatR;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
|
||||
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
|
||||
using Sufi.Demo.PeopleDirectory.Shared.Wrapper;
|
||||
|
||||
namespace Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetAll
|
||||
{
|
||||
public class GetAllContactsQuery : IRequest<IResult<List<GetAllContactsResponse>>>
|
||||
{
|
||||
}
|
||||
|
||||
public class GetAllContactsQueryHandler(
|
||||
IUnitOfWork<int> unitOfWork,
|
||||
IMapper mapper,
|
||||
IAppCache appCache
|
||||
) : IRequestHandler<GetAllContactsQuery, IResult<List<GetAllContactsResponse>>>
|
||||
{
|
||||
public async Task<IResult<List<GetAllContactsResponse>>> Handle(GetAllContactsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
Task<List<Contact>> allContactsFunc() => unitOfWork.Repository<Contact>().GetAllAsync();
|
||||
|
||||
var allContacts = await appCache.GetOrAddAsync(
|
||||
"contact_all",
|
||||
async token => await allContactsFunc(),
|
||||
absoluteExpireTime: TimeSpan.FromMinutes(2),
|
||||
tags: ["contacts"]
|
||||
);
|
||||
|
||||
var mappedContacts = mapper.Map<List<GetAllContactsResponse>>(allContacts);
|
||||
return await Result<List<GetAllContactsResponse>>.SuccessAsync(mappedContacts);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetAll
|
||||
{
|
||||
public record GetAllContactsResponse
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string UserName { get; set; } = null!;
|
||||
public string Phone { get; set; } = null!;
|
||||
public string Email { get; set; } = null!;
|
||||
public string SkillSets { get; set; } = null!;
|
||||
public string Hobby { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using AutoMapper;
|
||||
using FluentValidation;
|
||||
using MediatR;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Repositories;
|
||||
using Sufi.Demo.PeopleDirectory.Application.Contracts.Services;
|
||||
using Sufi.Demo.PeopleDirectory.Domain.Entities.Misc;
|
||||
using Sufi.Demo.PeopleDirectory.Shared.Wrapper;
|
||||
|
||||
namespace Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetById
|
||||
{
|
||||
public class GetContactByIdQuery : IRequest<IResult<GetContactByIdResponse>>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public sealed class GetContactByIdQueryValidator : AbstractValidator<GetContactByIdQuery>
|
||||
{
|
||||
public GetContactByIdQueryValidator()
|
||||
{
|
||||
RuleFor(v => v.Id)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("A valid Id is required.");
|
||||
}
|
||||
}
|
||||
|
||||
public class GetContactByIdQueryHandler(
|
||||
IUnitOfWork<int> unitOfWork,
|
||||
IMapper mapper,
|
||||
IAppCache appCache
|
||||
) : IRequestHandler<GetContactByIdQuery, IResult<GetContactByIdResponse>>
|
||||
{
|
||||
public async Task<IResult<GetContactByIdResponse>> Handle(GetContactByIdQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
Task<Contact?> getContactByIdFunc() => unitOfWork.Repository<Contact>().GetByIdAsync(request.Id);
|
||||
|
||||
var contact = await appCache.GetOrAddAsync(
|
||||
$"contact_{request.Id}",
|
||||
async token => await getContactByIdFunc(),
|
||||
absoluteExpireTime: TimeSpan.FromMinutes(2),
|
||||
tags: ["contacts"]
|
||||
);
|
||||
|
||||
var mappedContact = mapper.Map<GetContactByIdResponse>(contact);
|
||||
return await Result<GetContactByIdResponse>.SuccessAsync(mappedContact);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetById
|
||||
{
|
||||
public record GetContactByIdResponse
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string UserName { get; set; } = null!;
|
||||
public string Phone { get; set; } = null!;
|
||||
public string Email { get; set; } = null!;
|
||||
public string SkillSets { get; set; } = null!;
|
||||
public string Hobby { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user