< Summary

Information
Class: LGDXRobotCloud.API.Services.Automation.FlowService
Assembly: LGDXRobotCloud.API
File(s): /builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Automation/FlowService.cs
Line coverage
89%
Covered lines: 159
Uncovered lines: 19
Coverable lines: 178
Total lines: 233
Line coverage: 89.3%
Branch coverage
73%
Covered branches: 22
Total branches: 30
Branch coverage: 73.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%44100%
GetFlowsAsync()100%22100%
GetFlowAsync()100%22100%
ValidateFlow()100%1010100%
CreateFlowAsync()100%11100%
UpdateFlowAsync()50%6680%
TestDeleteFlowAsync()100%22100%
DeleteFlowAsync()0%620%
SearchFlowsAsync()50%22100%

File(s)

/builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Automation/FlowService.cs

#LineLine coverage
 1using LGDXRobotCloud.API.Exceptions;
 2using LGDXRobotCloud.API.Services.Administration;
 3using LGDXRobotCloud.Data.DbContexts;
 4using LGDXRobotCloud.Data.Entities;
 5using LGDXRobotCloud.Data.Models.Business.Administration;
 6using LGDXRobotCloud.Data.Models.Business.Automation;
 7using LGDXRobotCloud.Utilities.Enums;
 8using LGDXRobotCloud.Utilities.Helpers;
 9using Microsoft.EntityFrameworkCore;
 10
 11namespace LGDXRobotCloud.API.Services.Automation;
 12
 13public interface IFlowService
 14{
 15  Task<(IEnumerable<FlowListBusinessModel>, PaginationHelper)> GetFlowsAsync(string? name, int pageNumber, int pageSize)
 16  Task<FlowBusinessModel> GetFlowAsync(int flowId);
 17  Task<FlowBusinessModel> CreateFlowAsync(FlowCreateBusinessModel flowCreateBusinessModel);
 18  Task<bool> UpdateFlowAsync(int flowId, FlowUpdateBusinessModel flowUpdateBusinessModel);
 19  Task<bool> TestDeleteFlowAsync(int flowId);
 20  Task<bool> DeleteFlowAsync(int flowId);
 21
 22  Task<IEnumerable<FlowSearchBusinessModel>> SearchFlowsAsync(string? name);
 23}
 24
 1925public class FlowService(
 1926    IActivityLogService activityLogService,
 1927    LgdxContext context
 1928  ) : IFlowService
 29{
 1930  private readonly IActivityLogService _activityLogService = activityLogService ?? throw new ArgumentNullException(nameo
 1931  private readonly LgdxContext _context = context ?? throw new ArgumentNullException(nameof(context));
 32
 33  public async Task<(IEnumerable<FlowListBusinessModel>, PaginationHelper)> GetFlowsAsync(string? name, int pageNumber, 
 434  {
 435    var query = _context.Flows as IQueryable<Flow>;
 436      if(!string.IsNullOrWhiteSpace(name))
 337      {
 338        name = name.Trim();
 339        query = query.Where(f => f.Name.ToLower().Contains(name.ToLower()));
 340      }
 441      var itemCount = await query.CountAsync();
 442      var PaginationHelper = new PaginationHelper(itemCount, pageNumber, pageSize);
 443      var flows = await query.AsNoTracking()
 444        .OrderBy(t => t.Id)
 445        .Skip(pageSize * (pageNumber - 1))
 446        .Take(pageSize)
 447        .Select(t => new FlowListBusinessModel {
 448          Id = t.Id,
 449          Name = t.Name,
 450        })
 451        .ToListAsync();
 452      return (flows, PaginationHelper);
 453  }
 54
 55  public async Task<FlowBusinessModel> GetFlowAsync(int flowId)
 256  {
 257    return await _context.Flows.Where(f => f.Id == flowId)
 258      .Include(f => f.FlowDetails
 259        .OrderBy(fd => fd.Order))
 260        .ThenInclude(fd => fd.Progress)
 261      .Include(f => f.FlowDetails)
 262        .ThenInclude(fd => fd.Trigger)
 263      .Select(f => new FlowBusinessModel {
 264        Id = f.Id,
 265        Name = f.Name,
 266        FlowDetails = f.FlowDetails.Select(fd => new FlowDetailBusinessModel {
 267          Id = fd.Id,
 268          Order = fd.Order,
 269          ProgressId = fd.Progress.Id,
 270          ProgressName = fd.Progress.Name,
 271          AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 272          TriggerId = fd.Trigger!.Id,
 273          TriggerName = fd.Trigger!.Name,
 274        }).OrderBy(fd => fd.Order).ToList(),
 275      })
 276      .FirstOrDefaultAsync()
 277        ?? throw new LgdxNotFound404Exception();
 178  }
 79
 80  private async Task ValidateFlow(HashSet<int> progressIds, HashSet<int> triggerIds)
 581  {
 882    var progresses = await _context.Progresses.Where(p => progressIds.Contains(p.Id)).ToDictionaryAsync(p => p.Id);
 2183    foreach (var progressId in progressIds)
 484    {
 485      if (progresses.TryGetValue(progressId, out var progress))
 386      {
 387        if (progress.Reserved)
 188        {
 189          throw new LgdxValidation400Expection("Progress", $"The Progress ID: {progressId} is reserved.");
 90        }
 291      }
 92      else
 193      {
 194        throw new LgdxValidation400Expection("Progress", $"The Progress Id: {progressId} is invalid.");
 95      }
 296    }
 497    var triggers = await _context.Triggers.Where(t => triggerIds.Contains(t.Id)).ToDictionaryAsync(t => t.Id);
 1298    foreach (var triggerId in triggerIds)
 299    {
 2100      if (!triggers.TryGetValue(triggerId, out var trigger))
 1101      {
 1102        throw new LgdxValidation400Expection("Trigger", $"The Trigger ID: {triggerId} is invalid.");
 103      }
 1104    }
 2105  }
 106
 107  public async Task<FlowBusinessModel> CreateFlowAsync(FlowCreateBusinessModel flowCreateBusinessModel)
 4108  {
 8109    HashSet<int> progressIds = flowCreateBusinessModel.FlowDetails.Select(d => d.ProgressId).ToHashSet();
 12110    HashSet<int> triggerIds = flowCreateBusinessModel.FlowDetails.Where(d => d.TriggerId != null).Select(d => d.TriggerI
 4111    await ValidateFlow(progressIds, triggerIds);
 1112    var flow = new Flow {
 1113      Name = flowCreateBusinessModel.Name,
 1114      FlowDetails = flowCreateBusinessModel.FlowDetails.Select(fd => new FlowDetail {
 1115        Order = fd.Order,
 1116        ProgressId = fd.ProgressId,
 1117        AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 1118        TriggerId = fd.TriggerId,
 1119      }).ToList(),
 1120    };
 1121    await _context.Flows.AddAsync(flow);
 1122    await _context.SaveChangesAsync();
 123
 1124    await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel
 1125    {
 1126      EntityName = nameof(Flow),
 1127      EntityId = flow.Id.ToString(),
 1128      Action = ActivityAction.Create,
 1129    });
 130
 1131    return new FlowBusinessModel
 1132    {
 1133      Id = flow.Id,
 1134      Name = flow.Name,
 1135      FlowDetails = flow.FlowDetails.Select(fd => new FlowDetailBusinessModel
 1136      {
 1137        Id = fd.Id,
 1138        Order = fd.Order,
 1139        ProgressId = fd.ProgressId,
 1140        ProgressName = fd.Progress.Name,
 1141        AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 1142        TriggerId = fd.TriggerId,
 1143        TriggerName = fd.Trigger?.Name,
 1144      }).ToList(),
 1145    };
 1146  }
 147
 148  public async Task<bool> UpdateFlowAsync(int flowId, FlowUpdateBusinessModel flowUpdateBusinessModel)
 2149  {
 2150    var flow = await _context.Flows.Where(f => f.Id == flowId)
 2151      .Include(f => f.FlowDetails
 2152        .OrderBy(fd => fd.Order))
 2153        .ThenInclude(fd => fd.Progress)
 2154      .Include(f => f.FlowDetails)
 2155        .ThenInclude(fd => fd.Trigger)
 2156      .FirstOrDefaultAsync()
 2157        ?? throw new LgdxNotFound404Exception();
 158
 1159    HashSet<int?> dbDetailIds = flowUpdateBusinessModel.FlowDetails.Where(d => d.Id != null).Select(d => d.Id).ToHashSet
 3160    foreach(var dtoDetailId in flowUpdateBusinessModel.FlowDetails.Where(d => d.Id != null).Select(d => d.Id))
 0161    {
 0162      if(!dbDetailIds.Contains((int)dtoDetailId!))
 0163      {
 0164        throw new LgdxValidation400Expection("FlowDetails", $"The Flow Detail ID {(int)dtoDetailId} is belongs to other 
 165      }
 0166    }
 1167    HashSet<int> progressIds = flowUpdateBusinessModel.FlowDetails.Select(d => d.ProgressId).ToHashSet();
 1168    HashSet<int> triggerIds = flowUpdateBusinessModel.FlowDetails.Where(d => d.TriggerId != null).Select(d => d.TriggerI
 1169    await ValidateFlow(progressIds, triggerIds);
 170
 1171    flow.Name = flowUpdateBusinessModel.Name;
 1172    flow.FlowDetails = flowUpdateBusinessModel.FlowDetails.Select(fd => new FlowDetail {
 1173        Id = (int)fd.Id!,
 1174        Order = fd.Order,
 1175        ProgressId = fd.ProgressId,
 1176        AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 1177        TriggerId = fd.TriggerId,
 1178      }).ToList();
 1179    await _context.SaveChangesAsync();
 180
 1181    await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel
 1182    {
 1183      EntityName = nameof(Flow),
 1184      EntityId = flowId.ToString(),
 1185      Action = ActivityAction.Update,
 1186    });
 187
 1188    return true;
 1189  }
 190
 191  public async Task<bool> TestDeleteFlowAsync(int flowId)
 2192  {
 2193    var dependencies = await _context.AutoTasks
 2194      .Where(a => a.FlowId == flowId)
 2195      .Where(a => a.CurrentProgressId != (int)ProgressState.Completed && a.CurrentProgressId != (int)ProgressState.Abort
 2196      .CountAsync();
 2197    if (dependencies > 0)
 1198    {
 1199      throw new LgdxValidation400Expection(nameof(flowId), $"This flow has been used by {dependencies} running/waiting/t
 200    }
 1201    return true;
 1202  }
 203
 204  public async Task<bool> DeleteFlowAsync(int flowId)
 0205  {
 0206    var result = await _context.Flows.Where(f => f.Id == flowId)
 0207      .ExecuteDeleteAsync() == 1;
 208
 0209    if (result)
 0210    {
 0211      await _activityLogService.CreateActivityLogAsync(new ActivityLogCreateBusinessModel
 0212      {
 0213        EntityName = nameof(Flow),
 0214        EntityId = flowId.ToString(),
 0215        Action = ActivityAction.Delete,
 0216      });
 0217    }
 0218    return result;
 0219  }
 220
 221  public async Task<IEnumerable<FlowSearchBusinessModel>> SearchFlowsAsync(string? name)
 5222  {
 5223    var n = name ?? string.Empty;
 5224    return await _context.Flows.AsNoTracking()
 5225      .Where(w => w.Name.ToLower().Contains(n.ToLower()))
 5226      .Take(10)
 5227      .Select(t => new FlowSearchBusinessModel {
 5228        Id = t.Id,
 5229        Name = t.Name,
 5230      })
 5231      .ToListAsync();
 5232  }
 233}