diff --git a/.github/workflows/c-sharp-postman-test.yaml b/.github/workflows/c-sharp-postman-test.yaml deleted file mode 100644 index 95855467..00000000 --- a/.github/workflows/c-sharp-postman-test.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: C-sharp Run Postman Tests Every 15 Minutes - -on: - workflow_dispatch: - schedule: - - cron: "*/15 * * * *" # Every 15 minutes - -jobs: - run-postman-tests: - runs-on: self-hosted - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - - - name: Install Newman - run: npm install -g newman - - - name: Run Newman tests - id: newman - run: | - newman run status/postman/telex_postmancollection_v1.json --reporters cli,json --reporter-json-export report.json - - - name: Upload newman report to API - run: | - curl -X POST "${{ vars.API_URL }}/api/v1/api-status" \ - -H "Content-Type: multipart/form-data" \ - -F "result_file=@report.json" diff --git a/Dockerfile b/Dockerfile index daf11f9c..b7b29ad4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,8 @@ WORKDIR /app # Copy the published output from the publish stage COPY --from=publish /app/publish . +# Copy the EmailTemplates folder +COPY src/Hng.Infrastructure/EmailTemplates /app/EmailTemplates # Command to run the application ENTRYPOINT ["dotnet", "Hng.Web.dll"] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 9f443c37..4fb787de 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -13,6 +13,14 @@ services: - postgres - redis restart: unless-stopped + deploy: + resources: + limits: + cpus: '0.17' + memory: 170M + reservations: + cpus: '0.08' + memory: 85M nginx: image: nginx:latest @@ -22,6 +30,7 @@ services: - backend volumes: - ./nginx.conf:/etc/nginx/nginx.conf + restart: unless-stopped postgres: image: postgres:latest @@ -35,6 +44,6 @@ services: redis: image: redis:latest - + restart: unless-stopped volumes: postgres_data: diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index 5ad2922c..cac47ac3 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -13,6 +13,14 @@ services: - postgres - redis restart: unless-stopped + deploy: + resources: + limits: + cpus: '0.17' + memory: 170M + reservations: + cpus: '0.08' + memory: 85M nginx: image: nginx:latest diff --git a/docker-compose.yml b/docker-compose.yml index a119558a..b03dd947 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,14 @@ services: - postgres - redis restart: unless-stopped + deploy: + resources: + limits: + cpus: '0.17' + memory: 170M + reservations: + cpus: '0.08' + memory: 85M nginx: image: nginx:latest diff --git a/src/Hng.Application.Test/Features/Blog/GetBlogByIdQueryShould.cs b/src/Hng.Application.Test/Features/Blog/GetBlogByIdQueryShould.cs index 798053ce..ae4c8bed 100644 --- a/src/Hng.Application.Test/Features/Blog/GetBlogByIdQueryShould.cs +++ b/src/Hng.Application.Test/Features/Blog/GetBlogByIdQueryShould.cs @@ -4,7 +4,7 @@ using Hng.Application.Features.Blogs.Dtos; using Hng.Application.Features.Blogs.Handlers; using Hng.Application.Features.Blogs.Queries; -using Hng.Application.Features.SuperAdmin.Dto; +using Hng.Application.Features.UserManagement.Dtos; using Hng.Domain.Entities; using Hng.Domain.Enums; using Hng.Infrastructure.Repository.Interface; diff --git a/src/Hng.Application.Test/Features/SuperAdmin/GetUsersBySearchQueryHandlerShould.cs b/src/Hng.Application.Test/Features/SuperAdmin/GetUsersBySearchQueryHandlerShould.cs index a0557e84..ce50ed3f 100644 --- a/src/Hng.Application.Test/Features/SuperAdmin/GetUsersBySearchQueryHandlerShould.cs +++ b/src/Hng.Application.Test/Features/SuperAdmin/GetUsersBySearchQueryHandlerShould.cs @@ -22,7 +22,7 @@ public GetUsersBySearchQueryHandlerShould() var config = new MapperConfiguration(cfg => { // Add your AutoMapper profiles here - cfg.CreateMap(); + cfg.CreateMap(); }); _mapper = config.CreateMapper(); diff --git a/src/Hng.Application.Test/Features/UserManagement/LoginUserCommandShould.cs b/src/Hng.Application.Test/Features/UserManagement/LoginUserCommandShould.cs index 814ccba2..ee5bc68e 100644 --- a/src/Hng.Application.Test/Features/UserManagement/LoginUserCommandShould.cs +++ b/src/Hng.Application.Test/Features/UserManagement/LoginUserCommandShould.cs @@ -1,9 +1,15 @@ using AutoMapper; +using Hng.Application.Features.UserManagement.Commands; using Hng.Application.Features.UserManagement.Dtos; +using Hng.Application.Features.UserManagement.Handlers; using Hng.Domain.Entities; using Hng.Infrastructure.Repository.Interface; using Hng.Infrastructure.Services.Interfaces; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; using Moq; +using System.Linq.Expressions; +using Xunit; namespace Hng.Application.Test.Features.UserManagement { @@ -11,6 +17,7 @@ public class LoginUserCommandShould { private readonly IMapper _mapper; private readonly Mock> _userRepositoryMock; + private readonly Mock> _lastLoginMock; private readonly Mock _passwordServiceMock; private readonly Mock _tokenServiceMock; private readonly User _user; @@ -27,6 +34,7 @@ public LoginUserCommandShould() _userRepositoryMock = new Mock>(); _passwordServiceMock = new Mock(); _tokenServiceMock = new Mock(); + _lastLoginMock = new Mock>(); _user = new User { @@ -35,7 +43,8 @@ public LoginUserCommandShould() FirstName = "John", LastName = "Doe", Password = "hashedpassword", - PasswordSalt = "salt" + PasswordSalt = "salt", + }; } @@ -43,16 +52,19 @@ public LoginUserCommandShould() //public async Task ReturnLoginResponseDtoForValidCredentials() //{ // // Arrange - // _userRepositoryMock.Setup(repo => repo.GetBySpec(It.IsAny>>(), It.IsAny>[]>())) - // .ReturnsAsync(_user); + // _userRepositoryMock.Setup(repo => repo.GetQueryableBySpec(It.IsAny>>()) + // .Include(It.IsAny>>>()) + // .ThenInclude(It.IsAny>>>()) + // .ThenInclude(It.IsAny>>()) + // .Include(It.IsAny>>>()).FirstOrDefaultAsync(CancellationToken.None)).ReturnsAsync(_user); // _passwordServiceMock.Setup(service => service.IsPasswordEqual("password", _user.PasswordSalt, _user.Password)) // .Returns(true); - // _tokenServiceMock.Setup(service => service.GenerateJwt(It.IsAny())) + // _tokenServiceMock.Setup(service => service.GenerateJwt(It.IsAny(), 5)) // .Returns("token"); - // var handler = new LoginUserCommandHandler(_userRepositoryMock.Object, _mapper, _passwordServiceMock.Object, _tokenServiceMock.Object); + // var handler = new LoginUserCommandHandler(_userRepositoryMock.Object,_lastLoginMock.Object, _mapper, _passwordServiceMock.Object, _tokenServiceMock.Object,new HttpContextAccessor()); // var command = new CreateUserLoginCommand(new UserLoginRequestDto // { diff --git a/src/Hng.Application/Features/SuperAdmin/Dto/UserDto.cs b/src/Hng.Application/Features/SuperAdmin/Dto/UserDto.cs index f8c1bdb0..422a81b8 100644 --- a/src/Hng.Application/Features/SuperAdmin/Dto/UserDto.cs +++ b/src/Hng.Application/Features/SuperAdmin/Dto/UserDto.cs @@ -2,7 +2,7 @@ namespace Hng.Application.Features.SuperAdmin.Dto { - public class UserDto + public class UserSuperDto { [JsonPropertyName("id")] public Guid Id { get; set; } diff --git a/src/Hng.Application/Features/SuperAdmin/Handlers/GetUsersBySearchQueryHandler.cs b/src/Hng.Application/Features/SuperAdmin/Handlers/GetUsersBySearchQueryHandler.cs index 6a9eef53..d986289f 100644 --- a/src/Hng.Application/Features/SuperAdmin/Handlers/GetUsersBySearchQueryHandler.cs +++ b/src/Hng.Application/Features/SuperAdmin/Handlers/GetUsersBySearchQueryHandler.cs @@ -8,7 +8,7 @@ namespace Hng.Application.Features.SuperAdmin.Handlers { - public class GetUsersBySearchQueryHandler : IRequestHandler> + public class GetUsersBySearchQueryHandler : IRequestHandler> { private readonly IRepository _userRepository; private readonly IMapper _mapper; @@ -19,15 +19,15 @@ public GetUsersBySearchQueryHandler(IRepository userRepository, IMapper ma _mapper = mapper; } - public async Task> Handle(GetUsersBySearchQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetUsersBySearchQuery request, CancellationToken cancellationToken) { var users = await _userRepository.GetAllAsync(); users = users.Where(v => request.usersQueryParameters.Email == null || v.Email.ToLower().Equals(request.usersQueryParameters.Email.ToLower())).ToList(); users = users.Where(v => request.usersQueryParameters.Firstname == null || v.FirstName.ToLower().Equals(request.usersQueryParameters.Firstname.ToLower())).ToList(); users = users.Where(v => request.usersQueryParameters.Lastname == null || v.LastName.ToLower().Equals(request.usersQueryParameters.Lastname.ToLower())).ToList(); - var mappedusers = _mapper.Map>(users); - var userSearchResult = PagedListDto.ToPagedList(mappedusers, request.usersQueryParameters.Offset, request.usersQueryParameters.Limit); + var mappedusers = _mapper.Map>(users); + var userSearchResult = PagedListDto.ToPagedList(mappedusers, request.usersQueryParameters.Offset, request.usersQueryParameters.Limit); return userSearchResult; } diff --git a/src/Hng.Application/Features/SuperAdmin/Mappers/AdminUsersMappingProfile.cs b/src/Hng.Application/Features/SuperAdmin/Mappers/AdminUsersMappingProfile.cs index 87839001..5d0cbdaf 100644 --- a/src/Hng.Application/Features/SuperAdmin/Mappers/AdminUsersMappingProfile.cs +++ b/src/Hng.Application/Features/SuperAdmin/Mappers/AdminUsersMappingProfile.cs @@ -7,7 +7,7 @@ public class AdminUsersMappingProfile : AutoMapper.Profile { public AdminUsersMappingProfile() { - CreateMap().ReverseMap(); + CreateMap().ReverseMap(); } } } diff --git a/src/Hng.Application/Features/SuperAdmin/Queries/GetUsersBySearchQuery.cs b/src/Hng.Application/Features/SuperAdmin/Queries/GetUsersBySearchQuery.cs index b207c3a3..d30eee9f 100644 --- a/src/Hng.Application/Features/SuperAdmin/Queries/GetUsersBySearchQuery.cs +++ b/src/Hng.Application/Features/SuperAdmin/Queries/GetUsersBySearchQuery.cs @@ -4,7 +4,7 @@ namespace Hng.Application.Features.SuperAdmin.Queries { - public class GetUsersBySearchQuery : IRequest> + public class GetUsersBySearchQuery : IRequest> { public GetUsersBySearchQuery(UsersQueryParameters parameters) { diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.Authentication.cs b/src/Hng.Graphql/Features/Mutations/Mutations.Authentication.cs new file mode 100644 index 00000000..8db56968 --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.Authentication.cs @@ -0,0 +1,73 @@ +using Hng.Application.Features.UserManagement.Commands; +using Hng.Application.Features.UserManagement.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + public async Task> Login(UserLoginRequestDto loginRequest, [FromServices] IMediator mediator) + { + var command = new CreateUserLoginCommand(loginRequest); + return await mediator.Send(command); + } + + public async Task> GoogleLogin(GoogleLoginRequestDto googleLoginRequest, [FromServices] IMediator mediator) + { + var command = new GoogleLoginCommand(googleLoginRequest.IdToken); + return await mediator.Send(command); + } + + public async Task UserSignUp(UserSignUpDto body, [FromServices] IMediator mediator) + { + var command = new UserSignUpCommand(body); + return await mediator.Send(command); + } + + [Authorize] + public async Task ChangePassword([FromBody] ChangePasswordCommand command, [FromServices] IMediator mediator) + { + var response = await mediator.Send(command); + return response.Value; + } + + public async Task ForgotPassword([FromBody] ForgotPasswordRequestDto request, [FromServices] IMediator mediator) + { + var response = await mediator.Send(new ForgotPasswordDto(request.Email, false)); + return response.Value; + } + + public async Task ForgotPasswordMobile([FromBody] ForgotPasswordRequestDto request, [FromServices] IMediator mediator) + { + var response = await mediator.Send(new ForgotPasswordDto(request.Email, true)); + return response.Value; + } + + public async Task VerifyForgotPasswordCode([FromBody] VerifyForgotPasswordCodeDto request, [FromServices] IMediator mediator) + { + var response = await mediator.Send(request); + return response.Value; + } + + public async Task PasswordResetMobile([FromBody] PasswordResetMobileDto request, [FromServices] IMediator mediator) + { + var respone = await mediator.Send(new PasswordResetMobileCommand(request)); + return respone.Value; + } + + [Authorize] + public async Task PasswordReset([FromBody] PasswordResetDto request, [FromServices] IMediator mediator) + { + var response = await mediator.Send(request); + return response.Value; + } + + public async Task> FacebookLogin([FromBody] FacebookLoginRequestDto request, [FromServices] IMediator mediator) + { + var command = new FacebookLoginCommand(request.AccessToken); + return await mediator.Send(command); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Mutations.BillingPlans.cs b/src/Hng.Graphql/Features/Mutations/Mutations.BillingPlans.cs similarity index 95% rename from src/Hng.Graphql/Mutations.BillingPlans.cs rename to src/Hng.Graphql/Features/Mutations/Mutations.BillingPlans.cs index f650b59c..e739d65e 100644 --- a/src/Hng.Graphql/Mutations.BillingPlans.cs +++ b/src/Hng.Graphql/Features/Mutations/Mutations.BillingPlans.cs @@ -1,6 +1,5 @@ using Hng.Application.Features.BillingPlans.Commands; using Hng.Application.Features.BillingPlans.Dtos; -using Hng.Application.Features.BillingPlans.Queries; using Hng.Application.Shared.Dtos; using HotChocolate.Authorization; using MediatR; diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.Blog.cs b/src/Hng.Graphql/Features/Mutations/Mutations.Blog.cs new file mode 100644 index 00000000..15d12aa3 --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.Blog.cs @@ -0,0 +1,32 @@ +using Hng.Application.Features.Blogs.Commands; +using Hng.Application.Features.Blogs.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + [Authorize] + public async Task CreateBlog(CreateBlogDto body, [FromServices] IMediator mediator) + { + var command = new CreateBlogCommand(body); + return await mediator.Send(command); + } + + [Authorize] + public async Task DeleteBlogById(Guid id, [FromServices] IMediator mediator) + { + var command = (new DeleteBlogByIdCommand(id)); + return await mediator.Send(command); + } + + [Authorize] + public async Task UpdateBlog(Guid id, UpdateBlogDto updateBlogDto, [FromServices] IMediator mediator) + { + var command = new UpdateBlogCommand(updateBlogDto, id); + return await mediator.Send(command); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.Categories.cs b/src/Hng.Graphql/Features/Mutations/Mutations.Categories.cs new file mode 100644 index 00000000..4baed271 --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.Categories.cs @@ -0,0 +1,27 @@ +using Hng.Application.Features.Categories.Commands; +using Hng.Application.Features.Categories.Dtos; +using Hng.Application.Shared.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + [Authorize] + public async Task> CreateCategory(CreateCategoryDto createCategoryDto, [FromServices] IMediator mediator) + { + var command = new CreateCategoryCommand(createCategoryDto.Name, createCategoryDto.Description, createCategoryDto.Slug); + return await mediator.Send(command); + } + + [Authorize] + public async Task> DeleteCategory(Guid id, [FromServices] IMediator mediator) + { + var command = new DeleteCategoryCommand(id); + return await mediator.Send(command); + } + + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.Comment.cs b/src/Hng.Graphql/Features/Mutations/Mutations.Comment.cs new file mode 100644 index 00000000..39e806f8 --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.Comment.cs @@ -0,0 +1,18 @@ +using Hng.Application.Features.Comments.Commands; +using Hng.Application.Features.Comments.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + [Authorize] + public async Task CreateComment(Guid blogId, CreateCommentDto body, [FromServices] IMediator mediator) + { + var command = new CreateCommentCommand(body, blogId); + return await mediator.Send(command); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.ContactUs.cs b/src/Hng.Graphql/Features/Mutations/Mutations.ContactUs.cs new file mode 100644 index 00000000..ca651b2f --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.ContactUs.cs @@ -0,0 +1,24 @@ +using Hng.Application.Features.ContactsUs.Command; +using Hng.Application.Features.ContactsUs.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + public async Task> CreateContactMessage(ContactUsRequestDto contactUsRequest, [FromServices] IMediator mediator) + { + var command = new CreateContactUsCommand(contactUsRequest); + return await mediator.Send(command); + } + + [Authorize] + public async Task> DeleteContactMessage(Guid id, [FromServices] IMediator mediator) + { + var command = new DeleteContactUsCommand(id); + return await mediator.Send(command); + } + } +} diff --git a/src/Hng.Graphql/Mutations.EmailTemplate.cs b/src/Hng.Graphql/Features/Mutations/Mutations.EmailTemplate.cs similarity index 100% rename from src/Hng.Graphql/Mutations.EmailTemplate.cs rename to src/Hng.Graphql/Features/Mutations/Mutations.EmailTemplate.cs diff --git a/src/Hng.Graphql/Mutations.Faq.cs b/src/Hng.Graphql/Features/Mutations/Mutations.Faq.cs similarity index 100% rename from src/Hng.Graphql/Mutations.Faq.cs rename to src/Hng.Graphql/Features/Mutations/Mutations.Faq.cs diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.HelpCenterTopics.cs b/src/Hng.Graphql/Features/Mutations/Mutations.HelpCenterTopics.cs new file mode 100644 index 00000000..6dcb9f69 --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.HelpCenterTopics.cs @@ -0,0 +1,32 @@ +using Hng.Application.Features.HelpCenter.Command; +using Hng.Application.Features.HelpCenter.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + [Authorize] + public async Task> CreateHelpCenterTopic([FromBody] CreateHelpCenterTopicRequestDto request, [FromServices] IMediator mediator) + { + var command = new CreateHelpCenterTopicCommand(request); + return await mediator.Send(command); + } + + [Authorize] + public async Task> UpdateHelpCenterTopic(Guid id, UpdateHelpCenterTopicRequestDto request, [FromServices] IMediator mediator) + { + var command = new UpdateHelpCenterTopicCommand(id, request); + return await mediator.Send(command); + } + + [Authorize] + public async Task> DeleteHelpCenterTopic(Guid id, [FromServices] IMediator mediator) + { + var command = new DeleteHelpCenterTopicCommand(id); + return await mediator.Send(command); + } + } +} diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.NewsLetter.cs b/src/Hng.Graphql/Features/Mutations/Mutations.NewsLetter.cs new file mode 100644 index 00000000..132291f5 --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.NewsLetter.cs @@ -0,0 +1,15 @@ +using Hng.Application.Features.NewsLetterSubscription.Commands; +using Hng.Application.Features.NewsLetterSubscription.Dtos; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + public async Task RegisterNewsLetterSubscriber(NewsLetterSubscriptionDto subscriber, [FromServices] IMediator mediator) + { + return await mediator.Send(new AddSubscriberCommand(subscriber)); + } + } +} diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.Notification.cs b/src/Hng.Graphql/Features/Mutations/Mutations.Notification.cs new file mode 100644 index 00000000..4e483064 --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.Notification.cs @@ -0,0 +1,69 @@ +using Hng.Application.Features.Notifications.Commands; +using Hng.Application.Features.Notifications.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql.Features.Mutations +{ + public partial class Mutations + { + /// + /// Notification Settings - Create User notification + /// + [Authorize] + public async Task CreateNotification(CreateNotificationDto command, [FromServices] IMediator mediator) + { + var createCommand = new CreateNotificationCommand(command); + return await mediator.Send(createCommand); + } + + + /// + /// Mark a single notification as read + /// + /// The ID of the notification + /// The request body containing is_read flag + /// Response indicating the result of the operation + [Authorize] + public async Task MarkNotificationAsRead(Guid notification_id, UpdateNotificationDto request, [FromServices] IMediator mediator) + { + var command = new UpdateNotificationCommand(notification_id, request.IsRead); + return await mediator.Send(command); + } + + /// + /// Mark all notification as read + /// + /// The request body containing is_read flag + /// Response indicating the result of the operation + [Authorize] + public async Task> MarkAllNotificationAsRead(UpdateNotificationDto request, [FromServices] IMediator mediator) + { + var command = new MarkAllCommand(request.IsRead); + return await mediator.Send(command); + + } + + /// + /// Clear all user's notifications (Read or Unread) + /// + [Authorize] + public async Task DeleteAllNotifications([FromServices] IMediator mediator) + { + var command = new DeleteAllNotificationsCommand(); + return await mediator.Send(command); + + } + + /// + /// Clear notification (Read or Unread) + /// + [Authorize] + public async Task DeleteNotificationById(Guid notification_id, [FromServices] IMediator mediator) + { + var command = new DeleteNotificationByIdCommand(notification_id); + return await mediator.Send(command); + } + } +} diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.NotificationSettings.cs b/src/Hng.Graphql/Features/Mutations/Mutations.NotificationSettings.cs new file mode 100644 index 00000000..b80f919b --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.NotificationSettings.cs @@ -0,0 +1,23 @@ +using Hng.Application.Features.Notifications.Commands; +using Hng.Application.Features.Notifications.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Mutations + { + /// + /// Notification Settings - User notification settings + /// + [Authorize] + public async Task CreateNotificationSettings([FromBody] CreateNotificationSettingsDto command, [FromServices] IMediator mediator) + { + { + var createCommand = new CreateNotificationSettingsCommand(command); + return await mediator.Send(createCommand); + } + } + } +} diff --git a/src/Hng.Graphql/Features/Mutations/Mutations.Subscriptions.cs b/src/Hng.Graphql/Features/Mutations/Mutations.Subscriptions.cs new file mode 100644 index 00000000..ac7e529c --- /dev/null +++ b/src/Hng.Graphql/Features/Mutations/Mutations.Subscriptions.cs @@ -0,0 +1,27 @@ +using Hng.Application.Features.Subscriptions.Commands; +using Hng.Application.Features.Subscriptions.Dtos.Requests; +using Hng.Application.Features.Subscriptions.Dtos.Responses; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + + +namespace Hng.Graphql +{ + public partial class Mutations + { + [Authorize] + public async Task SubscribeFreePlan(SubscribeFreePlan command, [FromServices] IMediator mediator) + { + var response = await mediator.Send(command); + return response.Value; + } + + [Authorize] + public async Task ActivateSubscription(Guid subscriptionId, [FromServices] IMediator mediator) + { + var command = new ActivateSubscriptionCommand(subscriptionId); + return await mediator.Send(command); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Features/Queries/Queries.Admin.cs b/src/Hng.Graphql/Features/Queries/Queries.Admin.cs new file mode 100644 index 00000000..b9f86dfe --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.Admin.cs @@ -0,0 +1,19 @@ +using Hng.Application.Features.SuperAdmin.Dto; +using Hng.Application.Features.SuperAdmin.Queries; +using Hng.Application.Shared.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Queries + { + [Authorize] + public async Task> GetUsersBySearch(UsersQueryParameters parameters, [FromServices] IMediator mediator) + { + var users = new GetUsersBySearchQuery(parameters); + return await mediator.Send(users); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Features/Queries/Queries.Authentication.cs b/src/Hng.Graphql/Features/Queries/Queries.Authentication.cs new file mode 100644 index 00000000..a0af88a9 --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.Authentication.cs @@ -0,0 +1,20 @@ +using Hng.Application.Features.UserManagement.Dtos; +using Hng.Application.Features.UserManagement.Queries; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Queries + { + [Authorize] + public async Task GetLoggedInUsersDetails([FromServices] IMediator mediator) + { + var query = new GetLoggedInUserDetailsQuery(); + return await mediator.Send(query); + } + + + } +} diff --git a/src/Hng.Graphql/Queries.BillingPlans.cs b/src/Hng.Graphql/Features/Queries/Queries.BillingPlans.cs similarity index 100% rename from src/Hng.Graphql/Queries.BillingPlans.cs rename to src/Hng.Graphql/Features/Queries/Queries.BillingPlans.cs diff --git a/src/Hng.Graphql/Features/Queries/Queries.Blog.cs b/src/Hng.Graphql/Features/Queries/Queries.Blog.cs new file mode 100644 index 00000000..17d2214c --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.Blog.cs @@ -0,0 +1,25 @@ +using Hng.Application.Features.Blogs.Dtos; +using Hng.Application.Features.Blogs.Queries; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Queries + { + [Authorize] + public async Task GetBlogById(Guid id, [FromServices] IMediator mediator) + { + var query = new GetBlogByIdQuery(id); + return await mediator.Send(query); + } + + [Authorize] + public async Task> GetBlogs([FromServices] IMediator mediator) + { + var blogs = new GetBlogsQuery(); + return await mediator.Send(blogs); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Features/Queries/Queries.Categories.cs b/src/Hng.Graphql/Features/Queries/Queries.Categories.cs new file mode 100644 index 00000000..93192bf8 --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.Categories.cs @@ -0,0 +1,26 @@ +using Hng.Application.Features.Categories.Dtos; +using Hng.Application.Features.Categories.Queries; +using Hng.Application.Shared.Dtos; +using HotChocolate.Authorization; +using Microsoft.AspNetCore.Mvc; +using MediatR; + +namespace Hng.Graphql +{ + public partial class Queries + { + [Authorize] + public async Task> GetCategory(Guid id, [FromServices] IMediator mediator) + { + var query = new GetCategoryByIdQuery(id); + return await mediator.Send(query); + } + + [Authorize] + public async Task>> GetAllCategories(GetAllCategoriesQueryParams queryParams, [FromServices] IMediator mediator) + { + var query = new GetAllCategoriesQuery(queryParams); + return await mediator.Send(query); + } + } +} diff --git a/src/Hng.Graphql/Features/Queries/Queries.Comment.cs b/src/Hng.Graphql/Features/Queries/Queries.Comment.cs new file mode 100644 index 00000000..a6b024bc --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.Comment.cs @@ -0,0 +1,18 @@ +using Hng.Application.Features.Comments.Dtos; +using Hng.Application.Features.Comments.Queries; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Queries + { + [Authorize] + public async Task> GetCommentsByBlogId(Guid blogId, [FromServices] IMediator mediator) + { + var query = new GetCommentsByBlogIdQuery(blogId); + return await mediator.Send(query); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Features/Queries/Queries.ContactUs.cs b/src/Hng.Graphql/Features/Queries/Queries.ContactUs.cs new file mode 100644 index 00000000..eda96d65 --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.ContactUs.cs @@ -0,0 +1,18 @@ +using Hng.Application.Features.ContactsUs.Dtos; +using Hng.Application.Features.ContactsUs.Queries; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Queries + { + [Authorize] + public async Task>> GetAllContactMessages([FromServices] IMediator mediator) + { + var query = new GetAllContactUsQuery(); + return await mediator.Send(query); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Queries.Dashboard.cs b/src/Hng.Graphql/Features/Queries/Queries.Dashboard.cs similarity index 100% rename from src/Hng.Graphql/Queries.Dashboard.cs rename to src/Hng.Graphql/Features/Queries/Queries.Dashboard.cs diff --git a/src/Hng.Graphql/Queries.EmailTemplate.cs b/src/Hng.Graphql/Features/Queries/Queries.EmailTemplate.cs similarity index 100% rename from src/Hng.Graphql/Queries.EmailTemplate.cs rename to src/Hng.Graphql/Features/Queries/Queries.EmailTemplate.cs diff --git a/src/Hng.Graphql/Queries.Faq.cs b/src/Hng.Graphql/Features/Queries/Queries.Faq.cs similarity index 100% rename from src/Hng.Graphql/Queries.Faq.cs rename to src/Hng.Graphql/Features/Queries/Queries.Faq.cs diff --git a/src/Hng.Graphql/Features/Queries/Queries.HelpCenterTopics.cs b/src/Hng.Graphql/Features/Queries/Queries.HelpCenterTopics.cs new file mode 100644 index 00000000..5d75ba5b --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.HelpCenterTopics.cs @@ -0,0 +1,28 @@ +using Hng.Application.Features.HelpCenter.Dtos; +using Hng.Application.Features.HelpCenter.Queries; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql +{ + public partial class Queries + { + public async Task> GetHelpCenterTopicById(Guid id, [FromServices] IMediator mediator) + { + var query = new GetHelpCenterTopicByIdQuery(id); + return await mediator.Send(query); + } + + public async Task>> GetHelpCenterTopics([FromServices] IMediator mediator) + { + var query = new GetHelpCenterTopicsQuery(); + return await mediator.Send(query); + } + + public async Task>> SearchTopics(SearchHelpCenterTopicsRequestDto request, [FromServices] IMediator mediator) + { + var query = new SearchHelpCenterTopicsQuery(request); + return await mediator.Send(query); + } + } +} diff --git a/src/Hng.Graphql/Features/Queries/Queries.Notification.cs b/src/Hng.Graphql/Features/Queries/Queries.Notification.cs new file mode 100644 index 00000000..4aad328d --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.Notification.cs @@ -0,0 +1,31 @@ +using Hng.Application.Features.Notifications.Dtos; +using Hng.Application.Features.Notifications.Queries; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + +namespace Hng.Graphql.Features.Queries +{ + public partial class Queries + { + /// + /// Retrieve user's notifications (Read + Unread) + /// + [Authorize] + public async Task GetAllNotifications([FromServices] IMediator mediator) + { + var query = new GetAllNotificationsQuery(); + return await mediator.Send(query); + } + + /// + /// Retrieve user's notifications (Read or Unread) + /// + [Authorize] + public async Task GetNotifications(bool? is_read, [FromServices] IMediator mediator) + { + var query = new GetNotificationsQuery(is_read); + return await mediator.Send(query); + } + } +} diff --git a/src/Hng.Graphql/Features/Queries/Queries.NotificationSetting.cs b/src/Hng.Graphql/Features/Queries/Queries.NotificationSetting.cs new file mode 100644 index 00000000..a9be2be2 --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.NotificationSetting.cs @@ -0,0 +1,27 @@ +using Hng.Application.Features.Notifications.Dtos; +using Hng.Application.Features.Notifications.Queries; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Hng.Graphql +{ + public partial class Queries + { + /// + /// Get Notification Settings by User ID + /// + [Authorize] + public async Task GetNotificationSettings(Guid user_id, [FromServices] IMediator mediator) + { + var query = new GetNotificationSettingsQuery(user_id); + return await mediator.Send(query); + + } + } +} diff --git a/src/Hng.Graphql/Features/Queries/Queries.Subscriptions.cs b/src/Hng.Graphql/Features/Queries/Queries.Subscriptions.cs new file mode 100644 index 00000000..c389a173 --- /dev/null +++ b/src/Hng.Graphql/Features/Queries/Queries.Subscriptions.cs @@ -0,0 +1,35 @@ +using Hng.Application.Features.Subscriptions.Dtos.Requests; +using Hng.Application.Features.Subscriptions.Dtos.Responses; +using Hng.Application.Features.Subscriptions.Queries; +using Hng.Application.Shared.Dtos; +using HotChocolate.Authorization; +using MediatR; +using Microsoft.AspNetCore.Mvc; + + +namespace Hng.Graphql +{ + public partial class Queries + { + [Authorize] + public async Task GetSubscriptionByOrganizationId(Guid organizationId, [FromServices] IMediator mediator) + { + var response = new GetSubscriptionByOrganizationIdQuery(organizationId); + return await mediator.Send(response); + } + + [Authorize] + public async Task GetSubscriptionByUserId(Guid userId, [FromServices] IMediator mediator) + { + var response = new GetSubscriptionByUserIdQuery(userId); + return await mediator.Send(response); + } + + [Authorize] + public async Task> GetSubscriptions(GetSubscriptionsQueryParameters parameters, [FromServices] IMediator mediator) + { + var subscriptions = new GetSubscriptionsQuery(parameters); + return await mediator.Send(subscriptions); + } + } +} \ No newline at end of file diff --git a/src/Hng.Graphql/Mutations.cs b/src/Hng.Graphql/Mutations.cs index b7dfc234..cd6299a9 100644 --- a/src/Hng.Graphql/Mutations.cs +++ b/src/Hng.Graphql/Mutations.cs @@ -1,7 +1,5 @@ using Hng.Application.Features.Roles.Command; using Hng.Application.Features.Roles.Dto; -using Hng.Application.Features.UserManagement.Commands; -using Hng.Application.Features.UserManagement.Dtos; using HotChocolate.Authorization; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -10,17 +8,7 @@ namespace Hng.Graphql { public partial class Mutations { - public async Task> Login(UserLoginRequestDto loginRequest, [FromServices] IMediator mediator) - { - var command = new CreateUserLoginCommand(loginRequest); - return await mediator.Send(command); - } - public async Task> GoogleLogin(GoogleLoginRequestDto googleLoginRequest, [FromServices] IMediator mediator) - { - var command = new GoogleLoginCommand(googleLoginRequest.IdToken); - return await mediator.Send(command); - } [Authorize] public async Task CreateRoleInOrganisation(Guid orgId, CreateRoleRequestDto request, [FromServices] IMediator mediator) diff --git a/src/Hng.Graphql/Queries.cs b/src/Hng.Graphql/Queries.cs index 76c1357b..a2481c02 100644 --- a/src/Hng.Graphql/Queries.cs +++ b/src/Hng.Graphql/Queries.cs @@ -1,7 +1,5 @@ using Hng.Application.Features.Roles.Dto; using Hng.Application.Features.Roles.Queries; -using Hng.Application.Features.UserManagement.Dtos; -using Hng.Application.Features.UserManagement.Queries; using HotChocolate.Authorization; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -10,12 +8,7 @@ namespace Hng.Graphql { public partial class Queries { - [Authorize] - public async Task GetLoggedInUsersDetails([FromServices] IMediator mediator) - { - var query = new GetLoggedInUserDetailsQuery(); - return await mediator.Send(query); - } + [Authorize] public async Task> GetRolesInOrganisation(Guid orgId, [FromServices] IMediator mediator) diff --git a/src/Hng.Web/Controllers/AdminController.cs b/src/Hng.Web/Controllers/AdminController.cs index cb97b6ef..163c5d6b 100644 --- a/src/Hng.Web/Controllers/AdminController.cs +++ b/src/Hng.Web/Controllers/AdminController.cs @@ -27,7 +27,7 @@ public AdminController(IMediator mediator) public async Task GetUsersBySearch([FromQuery] UsersQueryParameters parameters) { var users = await _mediator.Send(new GetUsersBySearchQuery(parameters)); - return Ok(new PaginatedResponseDto> { Data = users, Metadata = users.MetaData }); + return Ok(new PaginatedResponseDto> { Data = users, Metadata = users.MetaData }); } } } diff --git a/src/Hng.Web/Controllers/HelpCenterTopicController.cs b/src/Hng.Web/Controllers/HelpCenterTopicController.cs index 98fc1d05..9148289c 100644 --- a/src/Hng.Web/Controllers/HelpCenterTopicController.cs +++ b/src/Hng.Web/Controllers/HelpCenterTopicController.cs @@ -3,7 +3,7 @@ using Hng.Application.Features.HelpCenter.Queries; using Hng.Application.Shared.Dtos; using MediatR; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Hng.Web.Controllers @@ -25,6 +25,7 @@ public HelpCenterTopicController(IMediator mediator) /// The details of the Help Center topic to create. /// A response with the creation result or an error message. [HttpPost] + [Authorize] [ProducesResponseType(typeof(HelpCenterResponseDto), StatusCodes.Status201Created)] [ProducesResponseType(typeof(FailureResponseDto), StatusCodes.Status400BadRequest)] public async Task CreateHelpCenterTopic([FromBody] CreateHelpCenterTopicRequestDto request)