Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Reports for Organization details feature in Aggregator #143 #177

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions src/1-BuildingBlocks/Contracts/Dtos/Tasks/GetTaskDto.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using TaskoMask.BuildingBlocks.Contracts.Dtos.Common;
using TaskoMask.BuildingBlocks.Contracts.Enums;
using TaskoMask.BuildingBlocks.Contracts.Resources;

namespace TaskoMask.BuildingBlocks.Contracts.Dtos.Tasks;
Expand All @@ -8,6 +9,7 @@ public class GetTaskDto : TaskBaseDto
{
[Display(Name = nameof(ContractsMetadata.OrganizationName), ResourceType = typeof(ContractsMetadata))]
public string CardName { get; set; }
public BoardCardType CardType { get; set; }

public string BoardId { get; set; }

Expand Down
10 changes: 10 additions & 0 deletions src/1-BuildingBlocks/Contracts/Protos/base.proto
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,14 @@ message GetCardGrpcResponse {
string ownerId = 8;
}

message GetOrganizationReportGrpcResponse {

CreationTimeGrpcResponse creationTime=1;
string projectsCount = 2;
string boardsCount = 3;
string toDoTasksCount = 4;
string doingTasksCount = 5;
string doneTasksCount = 6;
string backlogTasksCount = 7;
string id = 8;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
syntax = "proto3";

package TaskoMask.BuildingBlocks.Contracts.Protos;

import "base.proto";

service GetOrganizationReportIdGrpcService {
rpc Handle (GetOrganizationReportIdGrpcRequest) returns (GetOrganizationReportGrpcResponse);
}

message GetOrganizationReportIdGrpcRequest {
string organizationId = 1;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using TaskoMask.BuildingBlocks.Contracts.Protos;
using TaskoMask.Services.Owners.Read.Api.Features.Organizations.GetOrganizationReportById;
using TaskoMask.Services.Owners.Read.Api.Features.Organizations.GetOrganizationsByOwnerId;
using TaskoMask.Services.Owners.Read.Api.Features.Projects.GetProjectById;
using TaskoMask.Services.Owners.Read.Api.Features.Projects.GetProjectsByOrganizationId;
Expand All @@ -16,5 +21,26 @@ public static void MapGrpcServices(this IEndpointRouteBuilder endpoints)
endpoints.MapGrpcService<GetOrganizationsByOwnerIdGrpcEndpoint>();
endpoints.MapGrpcService<GetProjectsByOrganizationIdGrpcEndpoint>();
endpoints.MapGrpcService<GetProjectByIdGrpcEndpoint>();
endpoints.MapGrpcService<GetOrganizationReportByIdGrpcEndpoint>();
}


public static void AddGrpcClients(this IServiceCollection services, IConfiguration configuration)
{
services.AddGrpcClient<GetBoardsByOrganizationIdGrpcService.GetBoardsByOrganizationIdGrpcServiceClient>(options =>
{
options.Address = new Uri(configuration["Url:Board-Read-Service"]);
});

services.AddGrpcClient<GetCardsByBoardIdGrpcService.GetCardsByBoardIdGrpcServiceClient>(options =>
{
options.Address = new Uri(configuration["Url:Board-Read-Service"]);
});

services.AddGrpcClient<GetTasksByCardIdGrpcService.GetTasksByCardIdGrpcServiceClient>(options =>
{
options.Address = new Uri(configuration["Url:Task-Read-Service"]);
});

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde

builder.Services.AddGrpcPreConfigured();

builder.Services.AddGrpcClients(builder.Configuration);

return builder.Build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using AutoMapper;
using Grpc.Core;
using System.Threading.Tasks;
using TaskoMask.BuildingBlocks.Application.Bus;
using TaskoMask.BuildingBlocks.Contracts.Protos;

namespace TaskoMask.Services.Owners.Read.Api.Features.Organizations.GetOrganizationReportById;


public class GetOrganizationReportByIdGrpcEndpoint : GetOrganizationReportIdGrpcService.GetOrganizationReportIdGrpcServiceBase
{
private readonly IInMemoryBus _inMemoryBus;
private readonly IMapper _mapper;

public GetOrganizationReportByIdGrpcEndpoint(IInMemoryBus inMemoryBus, IMapper mapper)
{
_inMemoryBus = inMemoryBus;
_mapper = mapper;
}

public override async Task<GetOrganizationReportGrpcResponse> Handle(GetOrganizationReportIdGrpcRequest request, ServerCallContext context)
{
var report = await _inMemoryBus.SendQuery(new GetOrganizationReportByIdRequest(request.OrganizationId));
return _mapper.Map<GetOrganizationReportGrpcResponse>(report.Value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
using AutoMapper;
using Grpc.Core;
using MediatR;
using MongoDB.Driver;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TaskoMask.BuildingBlocks.Application.Exceptions;
using TaskoMask.BuildingBlocks.Application.Queries;
using TaskoMask.BuildingBlocks.Contracts.Dtos.Boards;
using TaskoMask.BuildingBlocks.Contracts.Dtos.Cards;
using TaskoMask.BuildingBlocks.Contracts.Dtos.Organizations;
using TaskoMask.BuildingBlocks.Contracts.Dtos.Tasks;
using TaskoMask.BuildingBlocks.Contracts.Enums;
using TaskoMask.BuildingBlocks.Contracts.Protos;
using TaskoMask.BuildingBlocks.Contracts.Resources;
using TaskoMask.BuildingBlocks.Contracts.ViewModels;
using TaskoMask.BuildingBlocks.Domain.Resources;
using TaskoMask.Services.Owners.Read.Api.Infrastructure.DbContext;
using static TaskoMask.BuildingBlocks.Contracts.Protos.GetBoardsByOrganizationIdGrpcService;
using static TaskoMask.BuildingBlocks.Contracts.Protos.GetCardsByBoardIdGrpcService;
using static TaskoMask.BuildingBlocks.Contracts.Protos.GetTasksByCardIdGrpcService;


namespace TaskoMask.Services.Owners.Read.Api.Features.Organizations.GetOrganizationReportById;

public class GetOrganizationReportByIdHandler : BaseQueryHandler, IRequestHandler<GetOrganizationReportByIdRequest, OrganizationReportDto>
{
#region Fields

private readonly OwnerReadDbContext _ownerReadDbContext;
private readonly GetBoardsByOrganizationIdGrpcServiceClient _getBoardsByOrganizationIdGrpcServiceClient;
private readonly GetCardsByBoardIdGrpcServiceClient _getCardsByBoardIdGrpcServiceClient;
private readonly GetTasksByCardIdGrpcServiceClient _getTasksByCardIdGrpcServiceClient;
#endregion

#region Ctors

public GetOrganizationReportByIdHandler(OwnerReadDbContext ownerReadDbContext,
GetBoardsByOrganizationIdGrpcServiceClient getBoardsByOrganizationIdGrpcServiceClient,
GetCardsByBoardIdGrpcServiceClient getCardsByBoardIdGrpcServiceClient,
GetTasksByCardIdGrpcServiceClient getTasksByCardIdGrpcServiceClient,
IMapper mapper)
: base(mapper)
{
_ownerReadDbContext = ownerReadDbContext;
_getBoardsByOrganizationIdGrpcServiceClient = getBoardsByOrganizationIdGrpcServiceClient;
_getCardsByBoardIdGrpcServiceClient = getCardsByBoardIdGrpcServiceClient;
_getTasksByCardIdGrpcServiceClient = getTasksByCardIdGrpcServiceClient;
}

#endregion

#region Handlers



/// <summary>
///
/// </summary>
public async Task<OrganizationReportDto> Handle(GetOrganizationReportByIdRequest request, CancellationToken cancellationToken)
{
var organizationReportDto = new OrganizationReportDto();
var organization = await _ownerReadDbContext.Organizations
.Find(e => e.Id == request.Id)
.FirstOrDefaultAsync(cancellationToken: cancellationToken);

if (organization == null)
throw new ApplicationException(ContractsMessages.Data_Not_exist, DomainMetadata.Organization);

#region Project Reports
var organizationProjects = await _ownerReadDbContext.Projects.Find(e => e.Id == organization.Id).ToListAsync(cancellationToken);
organizationReportDto.ProjectsCount = organizationProjects?.Count ?? 0;
#endregion

#region Board Reports

var boards = (await GetBoardsAsync(organization.Id)).ToList();
organizationReportDto.BoardsCount = boards.Any() ? boards.Count : 0;
#endregion

#region Task Reports
long toDoTasksCount = 0;
long doingTasksCount = 0;
long doneTasksCount = 0;
long backlogTasksCount = 0;

var allBoardTasks = await GetAllTasksForBoardsAsync(boards, cancellationToken);

foreach (var boardTask in allBoardTasks)
{
foreach (var cardTask in boardTask.CardTasks)
{
foreach (var task in cardTask.Tasks)
{
switch (task.CardType)
{
case BoardCardType.ToDo:
toDoTasksCount++;
break;
case BoardCardType.Doing:
doingTasksCount++;
break;
case BoardCardType.Done:
doneTasksCount++;
break;
case BoardCardType.Backlog:
backlogTasksCount++;
break;
}
}
}
}

organizationReportDto.ToDoTasksCount = toDoTasksCount;
organizationReportDto.DoingTasksCount = doingTasksCount;
organizationReportDto.DoneTasksCount = doneTasksCount;
organizationReportDto.BacklogTasksCount = backlogTasksCount;


#endregion

return organizationReportDto;
}

#endregion

#region Private Methods
private async Task<IEnumerable<BoardTasksViewModel>> GetAllTasksForBoardsAsync(IEnumerable<GetBoardDto> boards, CancellationToken cancellationToken)
{
var boardTasks = new List<BoardTasksViewModel>();

foreach (var board in boards)
{
var cards = await GetCardsAsync(board.Id, cancellationToken);
var cardTasks = new List<CardDetailsViewModel>();

foreach (var card in cards)
{
var tasks = await GetTasksAsync(card.Card.Id);
cardTasks.Add(new CardDetailsViewModel
{
Card = card.Card,
Tasks = tasks
});
}

boardTasks.Add(new BoardTasksViewModel
{
Board = board,
CardTasks = cardTasks
});
}

return boardTasks;
}

public class BoardTasksViewModel
{
public GetBoardDto Board { get; set; }
public IEnumerable<CardDetailsViewModel> CardTasks { get; set; }
}

private async Task<IEnumerable<CardDetailsViewModel>> GetCardsAsync(string boardId, CancellationToken cancellationToken)
{
var cards = new List<CardDetailsViewModel>();

var cardsGrpcCall = _getCardsByBoardIdGrpcServiceClient.Handle(new GetCardsByBoardIdGrpcRequest { BoardId = boardId });

while (await cardsGrpcCall.ResponseStream.MoveNext(cancellationToken))
{
var currentCardGrpcResponse = cardsGrpcCall.ResponseStream.Current;

cards.Add(
new CardDetailsViewModel { Card = MapToCard(currentCardGrpcResponse), Tasks = await GetTasksAsync(currentCardGrpcResponse.Id), }
);
}

return cards.AsEnumerable();
}

private async Task<IEnumerable<GetTaskDto>> GetTasksAsync(string cardId)
{
var tasks = new List<GetTaskDto>();

var tasksGrpcCall = _getTasksByCardIdGrpcServiceClient.Handle(new GetTasksByCardIdGrpcRequest { CardId = cardId });

await foreach (var response in tasksGrpcCall.ResponseStream.ReadAllAsync())
tasks.Add(_mapper.Map<GetTaskDto>(response));

return tasks;
}

private async Task<IEnumerable<GetBoardDto>> GetBoardsAsync(string organizationId)
{
var boards = new List<GetBoardDto>();
var boardsGrpcCall = _getBoardsByOrganizationIdGrpcServiceClient.Handle(
new GetBoardsByOrganizationIdGrpcRequest { OrganizationId = organizationId }
);

await foreach (var response in boardsGrpcCall.ResponseStream.ReadAllAsync())
boards.Add(_mapper.Map<GetBoardDto>(response));

return boards;
}

private GetCardDto MapToCard(GetCardGrpcResponse cardGrpcResponse)
{
return _mapper.Map<GetCardDto>(cardGrpcResponse);
}

#endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using TaskoMask.BuildingBlocks.Application.Queries;
using TaskoMask.BuildingBlocks.Contracts.Dtos.Organizations;

namespace TaskoMask.Services.Owners.Read.Api.Features.Organizations.GetOrganizationReportById;


public class GetOrganizationReportByIdRequest : BaseQuery<OrganizationReportDto>
{
public GetOrganizationReportByIdRequest(string id)
{
Id = id;
}

public string Id { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using TaskoMask.BuildingBlocks.Application.Bus;
using TaskoMask.BuildingBlocks.Contracts.Dtos.Organizations;
using TaskoMask.BuildingBlocks.Contracts.Helpers;
using TaskoMask.BuildingBlocks.Contracts.Services;
using TaskoMask.BuildingBlocks.Web.MVC.Controllers;

namespace TaskoMask.Services.Owners.Read.Api.Features.Organizations.GetOrganizationReportById;



[Authorize("user-read-access")]
[Tags("Organizations")]
public class GetOrganizationReportByIdRestEndpoint : BaseApiController
{
public GetOrganizationReportByIdRestEndpoint(IAuthenticatedUserService authenticatedUserService, IInMemoryBus inMemoryBus)
: base(authenticatedUserService, inMemoryBus) { }

/// <summary>
/// get organization Report
/// </summary>
[HttpGet]
[Route("organizations/report")]
public async Task<Result<OrganizationReportDto>> Get(string organizationId)
{
return await _inMemoryBus.SendQuery(new GetOrganizationReportByIdRequest(organizationId));
}
}
Loading