Skip to content
Merged
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
1 change: 1 addition & 0 deletions PrismaDotnetApi/PrismaApi.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public static void Main(string[] args)

builder.Services.AddScoped<ITableRebuildingService, TableRebuildingService>();
builder.Services.AddScoped<IProjectService, ProjectService>();
builder.Services.AddScoped<IInfluenceDiagramService, InfluenceDiagramService>();
builder.Services.AddScoped<IIssueService, IssueService>();
builder.Services.AddScoped<INodeService, NodeService>();
builder.Services.AddScoped<INodeStyleService, NodeStyleService>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using PrismaApi.Domain.Dtos;

namespace PrismaApi.Application.Interfaces.Services;
public interface IInfluenceDiagramService
{
Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default);
Task<InfluenceDiagramDto> GetRestrictedInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ public interface IProjectService
Task<List<ProjectOutgoingDto>> GetAllAsync(UserOutgoingDto user, CancellationToken ct = default);
Task<List<PopulatedProjectDto>> GetPopulatedAsync(List<Guid> ids, UserOutgoingDto user, CancellationToken ct = default);
Task<List<PopulatedProjectDto>> GetAllPopulatedAsync(UserOutgoingDto user, CancellationToken ct = default);
Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default);
}
43 changes: 17 additions & 26 deletions PrismaDotnetApi/PrismaApi.Application/Services/FastApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ namespace PrismaApi.Application.Services;
public class FastApiService : IFastApiService
{
private readonly HttpClient _httpClient;
private readonly IProjectService _projectService;
private readonly IInfluenceDiagramService _influenceDiagramService;
private readonly IConfiguration _configuration;
public FastApiService(HttpClient httpClient, IProjectService projectService, IConfiguration configuration)
public FastApiService(HttpClient httpClient, IInfluenceDiagramService influenceDiagramService, IConfiguration configuration)
{
_httpClient = httpClient;
_httpClient.Timeout = TimeSpan.FromSeconds(180);
_projectService = projectService;
_influenceDiagramService = influenceDiagramService;
_configuration = configuration;
}

Expand Down Expand Up @@ -56,28 +56,22 @@ public async Task<ApiResponseDto> CallDownstreamFastApiPostAsync(string endpoint

public async Task<ApiResponseDto> SendInfluenceDiagramToFastApiAsync(Guid projectId, string endpoint, UserOutgoingDto user, CancellationToken ct = default)
{
var influenceDiagram = await _projectService.GetInfluenceDiagramAsync(projectId, user, ct);
// make a clone to avoid mutating the chached influence diagram in the project service
var influenceDiagramClone = influenceDiagram.DeepClone();
influenceDiagramClone.ApplyRestrictions();
var content = new StringContent(JsonSerializer.Serialize(influenceDiagramClone), Encoding.UTF8, "application/json");
var influenceDiagram = await _influenceDiagramService.GetRestrictedInfluenceDiagramAsync(projectId, user, ct);
var content = new StringContent(JsonSerializer.Serialize(influenceDiagram), Encoding.UTF8, "application/json");
return await CallDownstreamFastApiPostAsync(endpoint, content, ct);
}

public async Task<ApiResponseDto> SendPartialInfluenceDiagramToFastApiAsync(Guid projectId, string endpoint, List<List<Guid>> paths, UserOutgoingDto user, CancellationToken ct = default)
{
var influenceDiagram = await _projectService.GetInfluenceDiagramAsync(projectId, user, ct);
// make a clone to avoid mutating the chached influence diagram in the project service
var influenceDiagramClone = influenceDiagram.DeepClone();
influenceDiagramClone.ApplyRestrictions();
var influenceDiagram = await _influenceDiagramService.GetRestrictedInfluenceDiagramAsync(projectId, user, ct);

var payload = new
{
issues = influenceDiagramClone.issues,
edges = influenceDiagramClone.edges,
discrete_probabilities = influenceDiagramClone.discreteProbabilities,
discrete_utilities = influenceDiagramClone.discreteUtilities,
restriction_tables = influenceDiagramClone.restrictionTables,
issues = influenceDiagram.issues,
edges = influenceDiagram.edges,
discrete_probabilities = influenceDiagram.discreteProbabilities,
discrete_utilities = influenceDiagram.discreteUtilities,
restriction_tables = influenceDiagram.restrictionTables,
paths
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
Expand All @@ -86,18 +80,15 @@ public async Task<ApiResponseDto> SendPartialInfluenceDiagramToFastApiAsync(Guid

public async Task<ApiResponseDto> SendInfluenceDiagramWithEvidenceToFastApiAsync(Guid projectId, string endpoint, List<EvidenceRequestDto> data, UserOutgoingDto user, CancellationToken ct = default)
{
var influenceDiagram = await _projectService.GetInfluenceDiagramAsync(projectId, user, ct);
// make a clone to avoid mutating the chached influence diagram in the project service
var influenceDiagramClone = influenceDiagram.DeepClone();
influenceDiagramClone.ApplyRestrictions();
var influenceDiagram = await _influenceDiagramService.GetRestrictedInfluenceDiagramAsync(projectId, user, ct);

var payload = new
{
issues = influenceDiagramClone.issues,
edges = influenceDiagramClone.edges,
discrete_probabilities = influenceDiagramClone.discreteProbabilities,
discrete_utilities = influenceDiagramClone.discreteUtilities,
restriction_tables = influenceDiagramClone.restrictionTables,
issues = influenceDiagram.issues,
edges = influenceDiagram.edges,
discrete_probabilities = influenceDiagram.discreteProbabilities,
discrete_utilities = influenceDiagram.discreteUtilities,
restriction_tables = influenceDiagram.restrictionTables,
evidence = data,
};
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Linq.Expressions;
using Microsoft.Extensions.Caching.Memory;
using PrismaApi.Application.Interfaces.Repositories;
using PrismaApi.Application.Interfaces.Services;
using PrismaApi.Application.Mapping;
using PrismaApi.Domain.Dtos;
using PrismaApi.Domain.Entities;
using PrismaApi.Domain.Extensions;
using PrismaApi.Infrastructure.Caching;

namespace PrismaApi.Application.Services;



public class InfluenceDiagramService : IInfluenceDiagramService
{
private readonly IIssueRepository _issueRepository;
private readonly IEdgeRepository _edgeRepository;
private readonly IDiscreteProbabilityRepository _discreteProbabilityRepository;
private readonly IDiscreteUtilityRepository _discreteUtilityRepository;
private readonly IRestrictionTableRepository _restrictionTableRepository;
private readonly IMemoryCache _cache;
public InfluenceDiagramService(
IIssueRepository issueRepository,
IEdgeRepository edgeRepository,
IDiscreteProbabilityRepository discreteProbabilityRepository,
IDiscreteUtilityRepository discreteUtilityRepository,
IRestrictionTableRepository restrictionTableRepository,
IMemoryCache cache)
{
_issueRepository = issueRepository;
_edgeRepository = edgeRepository;
_discreteProbabilityRepository = discreteProbabilityRepository;
_discreteUtilityRepository = discreteUtilityRepository;
_restrictionTableRepository = restrictionTableRepository;
_cache = cache;
}
public async Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default)
{
var cachedDiagram = _cache.GetCacheItemAsInfluenceDiagram(projectId, user);
if (cachedDiagram != null)
{
return cachedDiagram;
}

var issueEntities = await _issueRepository.GetIssuesInInfluenceDiagram(projectId, IssuesUserFilter(user), ct);
var edgeEntities = await _edgeRepository.GetEdgesInInfluenceDiagram(projectId, EdgesUserFilter(user), ct);
var discreteProbabilities = await _discreteProbabilityRepository.GetAllAsync(filterPredicate: e => e.Uncertainty!.Issue!.ProjectId == projectId, ct: ct);
var discreteUtilities = await _discreteUtilityRepository.GetAllAsync(filterPredicate: e => e.Utility!.Issue!.ProjectId == projectId, ct: ct);
var restrictionTables = await _restrictionTableRepository.GetAllAsync(filterPredicate: e => e.ProjectId == projectId, ct: ct);
var diagram = new InfluenceDiagramDto
{
projectId = projectId,
issues = issueEntities.ToOutgoingDtos(),
edges = edgeEntities.ToOutgoingDtos(),
discreteProbabilities = discreteProbabilities.ToDtos(),
discreteUtilities = discreteUtilities.ToDtos(),
restrictionTables = restrictionTables.ToOutgoingDtos(),
};

_cache.AddCacheItem(new CacheItem { CacheKey = CacheKeys.GetInfluenceDiagramKey(projectId) }, CacheConstants.DefaultMediumQueryCacheInTimeSpan, diagram);
return diagram;
}

public async Task<InfluenceDiagramDto> GetRestrictedInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default)
{
var diagram = await GetInfluenceDiagramAsync(projectId, user, ct);
var influenceDiagramDto = diagram.DeepClone();
influenceDiagramDto.ApplyRestrictions();
return influenceDiagramDto;
}

private static Expression<Func<Issue, bool>> IssuesUserFilter(UserOutgoingDto user)
=> e => e.Project!.Public || e.Project!.ProjectRoles.Any(p => p.UserId == user.Id);

private static Expression<Func<Edge, bool>> EdgesUserFilter(UserOutgoingDto user)
=> e => (e.HeadNode!.Issue!.Project!.Public || e.HeadNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id)) && (e.TailNode!.Issue!.Project!.Public || e.TailNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id));
}
48 changes: 0 additions & 48 deletions PrismaDotnetApi/PrismaApi.Application/Services/ProjectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,22 @@ public class ProjectService : IProjectService
{
private readonly IProjectRepository _projectRepository;
private readonly IProjectRoleRepository _projectRoleRepository;
private readonly IIssueRepository _issueRepository;
private readonly IEdgeRepository _edgeRepository;
private readonly IUserRepository _userRepository;
private readonly IBoardSheetRepository _boardSheetRepository;
private readonly IDiscreteProbabilityRepository _discreteProbabilityRepository;
private readonly IDiscreteUtilityRepository _discreteUtilityRepository;
private readonly IRestrictionTableRepository _restrictionTableRepository;
private readonly IMemoryCache _cache;

public ProjectService(
IProjectRepository projectRepository,
IProjectRoleRepository projectRoleRepository,
IIssueRepository issueRepository,
IEdgeRepository edgeRepository,
IUserRepository userRepository,
IBoardSheetRepository boardSheetRepository,
IDiscreteProbabilityRepository discreteProbabilityRepository,
IDiscreteUtilityRepository discreteUtilityRepository,
IRestrictionTableRepository restrictionTableRepository,
IMemoryCache cache)
{
_projectRepository = projectRepository;
_projectRoleRepository = projectRoleRepository;
_issueRepository = issueRepository;
_edgeRepository = edgeRepository;
_userRepository = userRepository;
_cache = cache;
_boardSheetRepository = boardSheetRepository;
_discreteProbabilityRepository = discreteProbabilityRepository;
_discreteUtilityRepository = discreteUtilityRepository;
_restrictionTableRepository = restrictionTableRepository;
}

public async Task<List<ProjectOutgoingDto>> CreateAsync(List<ProjectCreateDto> dtos, bool createDefaultRole, UserOutgoingDto userDto, CancellationToken ct = default)
Expand Down Expand Up @@ -124,33 +109,6 @@ public async Task<List<PopulatedProjectDto>> GetAllPopulatedAsync(UserOutgoingDt
return projects.ToPopulatedDtos();
}

public async Task<InfluenceDiagramDto> GetInfluenceDiagramAsync(Guid projectId, UserOutgoingDto user, CancellationToken ct = default)
{
var cachedDiagram = _cache.GetCacheItemAsInfluenceDiagram(projectId, user);
if (cachedDiagram != null)
{
return cachedDiagram;
}

var issueEntities = await _issueRepository.GetIssuesInInfluenceDiagram(projectId, IssuesUserFilter(user), ct);
var edgeEntities = await _edgeRepository.GetEdgesInInfluenceDiagram(projectId, EdgesUserFilter(user), ct);
var discreteProbabilities = await _discreteProbabilityRepository.GetAllAsync(filterPredicate: e => e.Uncertainty!.Issue!.ProjectId == projectId, ct: ct);
var discreteUtilities = await _discreteUtilityRepository.GetAllAsync(filterPredicate: e => e.Utility!.Issue!.ProjectId == projectId, ct: ct);
var restrictionTables = await _restrictionTableRepository.GetAllAsync(filterPredicate: e => e.ProjectId == projectId, ct: ct);
var diagram = new InfluenceDiagramDto
{
projectId = projectId,
issues = issueEntities.ToOutgoingDtos(),
edges = edgeEntities.ToOutgoingDtos(),
discreteProbabilities = discreteProbabilities.ToDtos(),
discreteUtilities = discreteUtilities.ToDtos(),
restrictionTables = restrictionTables.ToOutgoingDtos(),
};

_cache.AddCacheItem(new CacheItem { CacheKey = CacheKeys.GetInfluenceDiagramKey(projectId) }, CacheConstants.DefaultMediumQueryCacheInTimeSpan, diagram);
return diagram;
}

private async Task EnsureDefaultSheetsExists(List<Guid> projectIds, UserOutgoingDto user, CancellationToken ct = default)
{
var existingSheets = await _boardSheetRepository.GetAllAsync(withTracking: false, filterPredicate: e => projectIds.Contains(e.ProjectId), ct: ct);
Expand All @@ -172,12 +130,6 @@ private async Task EnsureDefaultSheetsExists(List<Guid> projectIds, UserOutgoing
private static Expression<Func<Project, bool>> UserFilter(UserOutgoingDto user)
=> e => e.Public || e.ProjectRoles.Any(p => p.UserId == user.Id);

private static Expression<Func<Issue, bool>> IssuesUserFilter(UserOutgoingDto user)
=> e => e.Project!.Public || e.Project!.ProjectRoles.Any(p => p.UserId == user.Id);

private static Expression<Func<Edge, bool>> EdgesUserFilter(UserOutgoingDto user)
=> e => (e.HeadNode!.Issue!.Project!.Public || e.HeadNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id)) && (e.TailNode!.Issue!.Project!.Public || e.TailNode!.Issue!.Project!.ProjectRoles.Any(p => p.UserId == user.Id));

private void RegisterPublicProjectsInCache(List<ProjectOutgoingDto> projects)
{
var publicProjectIds = projects.Where(p => p.Public).Select(p => p.Id).ToList();
Expand Down
Loading
Loading