< 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
94%
Covered lines: 141
Uncovered lines: 9
Coverable lines: 150
Total lines: 199
Line coverage: 94%
Branch coverage
80%
Covered branches: 21
Total branches: 26
Branch coverage: 80.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

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

File(s)

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

#LineLine coverage
 1using LGDXRobotCloud.API.Exceptions;
 2using LGDXRobotCloud.Data.DbContexts;
 3using LGDXRobotCloud.Data.Entities;
 4using LGDXRobotCloud.Data.Models.Business.Automation;
 5using LGDXRobotCloud.Utilities.Enums;
 6using LGDXRobotCloud.Utilities.Helpers;
 7using Microsoft.EntityFrameworkCore;
 8
 9namespace LGDXRobotCloud.API.Services.Automation;
 10
 11public interface IFlowService
 12{
 13  Task<(IEnumerable<FlowListBusinessModel>, PaginationHelper)> GetFlowsAsync(string? name, int pageNumber, int pageSize)
 14  Task<FlowBusinessModel> GetFlowAsync(int flowId);
 15  Task<FlowBusinessModel> CreateFlowAsync(FlowCreateBusinessModel flowCreateBusinessModel);
 16  Task<bool> UpdateFlowAsync(int flowId, FlowUpdateBusinessModel flowUpdateBusinessModel);
 17  Task<bool> TestDeleteFlowAsync(int flowId);
 18  Task<bool> DeleteFlowAsync(int flowId);
 19
 20  Task<IEnumerable<FlowSearchBusinessModel>> SearchFlowsAsync(string? name);
 21}
 22
 1923public class FlowService(LgdxContext context) : IFlowService
 24{
 1925  private readonly LgdxContext _context = context ?? throw new ArgumentNullException(nameof(context));
 26
 27  public async Task<(IEnumerable<FlowListBusinessModel>, PaginationHelper)> GetFlowsAsync(string? name, int pageNumber, 
 428  {
 429    var query = _context.Flows as IQueryable<Flow>;
 430      if(!string.IsNullOrWhiteSpace(name))
 331      {
 332        name = name.Trim();
 333        query = query.Where(f => f.Name.ToLower().Contains(name.ToLower()));
 334      }
 435      var itemCount = await query.CountAsync();
 436      var PaginationHelper = new PaginationHelper(itemCount, pageNumber, pageSize);
 437      var flows = await query.AsNoTracking()
 438        .OrderBy(t => t.Id)
 439        .Skip(pageSize * (pageNumber - 1))
 440        .Take(pageSize)
 441        .Select(t => new FlowListBusinessModel {
 442          Id = t.Id,
 443          Name = t.Name,
 444        })
 445        .ToListAsync();
 446      return (flows, PaginationHelper);
 447  }
 48
 49  public async Task<FlowBusinessModel> GetFlowAsync(int flowId)
 250  {
 251    return await _context.Flows.Where(f => f.Id == flowId)
 252      .Include(f => f.FlowDetails
 253        .OrderBy(fd => fd.Order))
 254        .ThenInclude(fd => fd.Progress)
 255      .Include(f => f.FlowDetails)
 256        .ThenInclude(fd => fd.Trigger)
 257      .Select(f => new FlowBusinessModel {
 258        Id = f.Id,
 259        Name = f.Name,
 260        FlowDetails = f.FlowDetails.Select(fd => new FlowDetailBusinessModel {
 261          Id = fd.Id,
 262          Order = fd.Order,
 263          ProgressId = fd.Progress.Id,
 264          ProgressName = fd.Progress.Name,
 265          AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 266          TriggerId = fd.Trigger!.Id,
 267          TriggerName = fd.Trigger!.Name,
 268        }).OrderBy(fd => fd.Order).ToList(),
 269      })
 270      .FirstOrDefaultAsync()
 271        ?? throw new LgdxNotFound404Exception();
 172  }
 73
 74  private async Task ValidateFlow(HashSet<int> progressIds, HashSet<int> triggerIds)
 575  {
 876    var progresses = await _context.Progresses.Where(p => progressIds.Contains(p.Id)).ToDictionaryAsync(p => p.Id);
 2177    foreach (var progressId in progressIds)
 478    {
 479      if (progresses.TryGetValue(progressId, out var progress))
 380      {
 381        if (progress.Reserved)
 182        {
 183          throw new LgdxValidation400Expection("Progress", $"The Progress ID: {progressId} is reserved.");
 84        }
 285      }
 86      else
 187      {
 188        throw new LgdxValidation400Expection("Progress", $"The Progress Id: {progressId} is invalid.");
 89      }
 290    }
 491    var triggers = await _context.Triggers.Where(t => triggerIds.Contains(t.Id)).ToDictionaryAsync(t => t.Id);
 1292    foreach (var triggerId in triggerIds)
 293    {
 294      if (!triggers.TryGetValue(triggerId, out var trigger))
 195      {
 196        throw new LgdxValidation400Expection("Trigger", $"The Trigger ID: {triggerId} is invalid.");
 97      }
 198    }
 299  }
 100
 101  public async Task<FlowBusinessModel> CreateFlowAsync(FlowCreateBusinessModel flowCreateBusinessModel)
 4102  {
 8103    HashSet<int> progressIds = flowCreateBusinessModel.FlowDetails.Select(d => d.ProgressId).ToHashSet();
 12104    HashSet<int> triggerIds = flowCreateBusinessModel.FlowDetails.Where(d => d.TriggerId != null).Select(d => d.TriggerI
 4105    await ValidateFlow(progressIds, triggerIds);
 1106    var flow = new Flow {
 1107      Name = flowCreateBusinessModel.Name,
 1108      FlowDetails = flowCreateBusinessModel.FlowDetails.Select(fd => new FlowDetail {
 1109        Order = fd.Order,
 1110        ProgressId = fd.ProgressId,
 1111        AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 1112        TriggerId = fd.TriggerId,
 1113      }).ToList(),
 1114    };
 1115    await _context.Flows.AddAsync(flow);
 1116    await _context.SaveChangesAsync();
 117
 1118    return new FlowBusinessModel {
 1119      Id = flow.Id,
 1120      Name = flow.Name,
 1121      FlowDetails = flow.FlowDetails.Select(fd => new FlowDetailBusinessModel {
 1122        Id = fd.Id,
 1123        Order = fd.Order,
 1124        ProgressId = fd.ProgressId,
 1125        ProgressName = fd.Progress.Name,
 1126        AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 1127        TriggerId = fd.TriggerId,
 1128        TriggerName = fd.Trigger?.Name,
 1129      }).ToList(),
 1130    };
 1131  }
 132
 133  public async Task<bool> UpdateFlowAsync(int flowId, FlowUpdateBusinessModel flowUpdateBusinessModel)
 2134  {
 2135    var flow = await _context.Flows.Where(f => f.Id == flowId)
 2136      .Include(f => f.FlowDetails
 2137        .OrderBy(fd => fd.Order))
 2138        .ThenInclude(fd => fd.Progress)
 2139      .Include(f => f.FlowDetails)
 2140        .ThenInclude(fd => fd.Trigger)
 2141      .FirstOrDefaultAsync()
 2142        ?? throw new LgdxNotFound404Exception();
 143
 1144    HashSet<int?> dbDetailIds = flowUpdateBusinessModel.FlowDetails.Where(d => d.Id != null).Select(d => d.Id).ToHashSet
 3145    foreach(var dtoDetailId in flowUpdateBusinessModel.FlowDetails.Where(d => d.Id != null).Select(d => d.Id))
 0146    {
 0147      if(!dbDetailIds.Contains((int)dtoDetailId!))
 0148      {
 0149        throw new LgdxValidation400Expection("FlowDetails", $"The Flow Detail ID {(int)dtoDetailId} is belongs to other 
 150      }
 0151    }
 1152    HashSet<int> progressIds = flowUpdateBusinessModel.FlowDetails.Select(d => d.ProgressId).ToHashSet();
 1153    HashSet<int> triggerIds = flowUpdateBusinessModel.FlowDetails.Where(d => d.TriggerId != null).Select(d => d.TriggerI
 1154    await ValidateFlow(progressIds, triggerIds);
 155
 1156    flow.Name = flowUpdateBusinessModel.Name;
 1157    flow.FlowDetails = flowUpdateBusinessModel.FlowDetails.Select(fd => new FlowDetail {
 1158        Id = (int)fd.Id!,
 1159        Order = fd.Order,
 1160        ProgressId = fd.ProgressId,
 1161        AutoTaskNextControllerId = fd.AutoTaskNextControllerId,
 1162        TriggerId = fd.TriggerId,
 1163      }).ToList();
 1164    await _context.SaveChangesAsync();
 1165    return true;
 1166  }
 167
 168  public async Task<bool> TestDeleteFlowAsync(int flowId)
 2169  {
 2170    var depeendencies = await _context.AutoTasks
 2171      .Where(a => a.FlowId == flowId)
 2172      .Where(a => a.CurrentProgressId != (int)ProgressState.Completed && a.CurrentProgressId != (int)ProgressState.Abort
 2173      .CountAsync();
 2174    if (depeendencies > 0)
 1175    {
 1176      throw new LgdxValidation400Expection(nameof(flowId), $"This flow has been used by {depeendencies} running/waiting/
 177    }
 1178    return true;
 1179  }
 180
 181  public async Task<bool> DeleteFlowAsync(int flowId)
 0182  {
 0183    return await _context.Flows.Where(f => f.Id == flowId)
 0184      .ExecuteDeleteAsync() >= 1;
 0185  }
 186
 187  public async Task<IEnumerable<FlowSearchBusinessModel>> SearchFlowsAsync(string? name)
 5188  {
 5189    var n = name ?? string.Empty;
 5190    return await _context.Flows.AsNoTracking()
 5191      .Where(w => w.Name.ToLower().Contains(n.ToLower()))
 5192      .Take(10)
 5193      .Select(t => new FlowSearchBusinessModel {
 5194        Id = t.Id,
 5195        Name = t.Name,
 5196      })
 5197      .ToListAsync();
 5198  }
 199}