< Summary

Information
Class: LGDXRobotCloud.API.Services.Automation.TriggerService
Assembly: LGDXRobotCloud.API
File(s): /builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Automation/TriggerService.cs
Line coverage
88%
Covered lines: 173
Uncovered lines: 22
Coverable lines: 195
Total lines: 263
Line coverage: 88.7%
Branch coverage
71%
Covered branches: 49
Total branches: 69
Branch coverage: 71%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%66100%
GetTriggersAsync()100%22100%
GetTriggerAsync()100%22100%
ValidateApiKey()100%44100%
CreateTriggerAsync()75%44100%
UpdateTriggerAsync()0%620%
TestDeleteTriggerAsync()100%22100%
DeleteTriggerAsync()100%210%
SearchTriggersAsync()50%22100%
GetRobotName(...)50%44100%
GetRealmName(...)50%44100%
GeneratePresetValue(...)70.58%171792.85%
InitialiseTriggerAsync()87.5%1616100%
RetryTriggerAsync()50%4494.11%

File(s)

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

#LineLine coverage
 1using System.Text.Json;
 2using LGDXRobotCloud.API.Exceptions;
 3using LGDXRobotCloud.API.Services.Administration;
 4using LGDXRobotCloud.Data.Contracts;
 5using LGDXRobotCloud.Data.DbContexts;
 6using LGDXRobotCloud.Data.Entities;
 7using LGDXRobotCloud.Data.Models.Business.Automation;
 8using LGDXRobotCloud.Utilities.Enums;
 9using LGDXRobotCloud.Utilities.Helpers;
 10using MassTransit;
 11using Microsoft.EntityFrameworkCore;
 12
 13namespace LGDXRobotCloud.API.Services.Automation;
 14
 15public interface ITriggerService
 16{
 17  Task<(IEnumerable<TriggerListBusinessModel>, PaginationHelper)> GetTriggersAsync(string? name, int pageNumber, int pag
 18  Task<TriggerBusinessModel> GetTriggerAsync(int triggerId);
 19  Task<TriggerBusinessModel> CreateTriggerAsync(TriggerCreateBusinessModel triggerCreateBusinessModel);
 20  Task<bool> UpdateTriggerAsync(int triggerId, TriggerUpdateBusinessModel triggerUpdateBusinessModel);
 21  Task<bool> TestDeleteTriggerAsync(int triggerId);
 22  Task<bool> DeleteTriggerAsync(int triggerId);
 23
 24  Task<IEnumerable<TriggerSearchBusinessModel>> SearchTriggersAsync(string? name);
 25
 26  Task InitialiseTriggerAsync(AutoTask autoTask, FlowDetail flowDetail);
 27  Task<bool> RetryTriggerAsync(AutoTask autoTask, Trigger trigger, string body);
 28}
 29
 1830public sealed class TriggerService (
 1831  IBus bus,
 1832  LgdxContext context,
 1833  IApiKeyService apiKeyService
 1834) : ITriggerService
 35{
 1836  private readonly IBus _bus = bus ?? throw new ArgumentNullException(nameof(bus));
 1837  private readonly LgdxContext _context = context ?? throw new ArgumentNullException(nameof(context));
 1838  private readonly IApiKeyService _apiKeyService = apiKeyService ?? throw new ArgumentNullException(nameof(apiKeyService
 39
 40  public async Task<(IEnumerable<TriggerListBusinessModel>, PaginationHelper)> GetTriggersAsync(string? name, int pageNu
 441  {
 442    var query = _context.Triggers as IQueryable<Trigger>;
 443    if(!string.IsNullOrWhiteSpace(name))
 344    {
 345      name = name.Trim();
 346      query = query.Where(t => t.Name.ToLower().Contains(name.ToLower()));
 347    }
 448    var itemCount = await query.CountAsync();
 449    var PaginationHelper = new PaginationHelper(itemCount, pageNumber, pageSize);
 450    var triggers = await query.AsNoTracking()
 451      .OrderBy(t => t.Id)
 452      .Skip(pageSize * (pageNumber - 1))
 453      .Take(pageSize)
 454      .Select(t => new TriggerListBusinessModel {
 455        Id = t.Id,
 456        Name = t.Name,
 457        Url = t.Url,
 458        HttpMethodId = t.HttpMethodId,
 459      })
 460      .ToListAsync();
 461    return (triggers, PaginationHelper);
 462  }
 63
 64  public async Task<TriggerBusinessModel> GetTriggerAsync(int triggerId)
 265  {
 266    return await _context.Triggers.Where(t => t.Id == triggerId)
 267      .Include(t => t.ApiKey)
 268      .Select(t => new TriggerBusinessModel {
 269        Id = t.Id,
 270        Name = t.Name,
 271        Url = t.Url,
 272        HttpMethodId = t.HttpMethodId,
 273        Body = t.Body,
 274        ApiKeyInsertLocationId = t.ApiKeyInsertLocationId,
 275        ApiKeyFieldName = t.ApiKeyFieldName,
 276        ApiKeyId = t.ApiKey!.Id,
 277        ApiKeyName = t.ApiKey!.Name,
 278      })
 279      .FirstOrDefaultAsync()
 280        ?? throw new LgdxNotFound404Exception();
 181  }
 82
 83  private async Task ValidateApiKey(int apiKeyId)
 384  {
 385    var apiKey = await _apiKeyService.GetApiKeyAsync(apiKeyId);
 386    if (apiKey == null)
 187    {
 188      throw new LgdxValidation400Expection(nameof(TriggerBusinessModel.Id), $"The API Key Id {apiKeyId} is invalid.");
 89    }
 290    else if (!apiKey.IsThirdParty)
 191    {
 192      throw new LgdxValidation400Expection(nameof(TriggerBusinessModel.Id), "Only third party API key is allowed.");
 93    }
 194  }
 95
 96  public async Task<TriggerBusinessModel> CreateTriggerAsync(TriggerCreateBusinessModel triggerCreateBusinessModel)
 397  {
 398    if (triggerCreateBusinessModel.ApiKeyId != null)
 399    {
 3100      await ValidateApiKey((int)triggerCreateBusinessModel.ApiKeyId);
 1101    }
 102
 1103    var trigger = new Trigger {
 1104      Name = triggerCreateBusinessModel.Name,
 1105      Url = triggerCreateBusinessModel.Url,
 1106      HttpMethodId = triggerCreateBusinessModel.HttpMethodId,
 1107      Body = triggerCreateBusinessModel.Body,
 1108      ApiKeyInsertLocationId = triggerCreateBusinessModel.ApiKeyInsertLocationId,
 1109      ApiKeyFieldName = triggerCreateBusinessModel.ApiKeyFieldName,
 1110      ApiKeyId = triggerCreateBusinessModel.ApiKeyId,
 1111    };
 1112    await _context.Triggers.AddAsync(trigger);
 1113    await _context.SaveChangesAsync();
 1114    return new TriggerBusinessModel {
 1115      Id = trigger.Id,
 1116      Name = trigger.Name,
 1117      Url = trigger.Url,
 1118      HttpMethodId = trigger.HttpMethodId,
 1119      Body = trigger.Body,
 1120      ApiKeyInsertLocationId = trigger.ApiKeyInsertLocationId,
 1121      ApiKeyFieldName = trigger.ApiKeyFieldName,
 1122      ApiKeyId = trigger.ApiKeyId,
 1123      ApiKeyName = trigger.ApiKey?.Name,
 1124    };
 1125  }
 126
 127  public async Task<bool> UpdateTriggerAsync(int triggerId, TriggerUpdateBusinessModel triggerUpdateBusinessModel)
 0128  {
 0129    if (triggerUpdateBusinessModel.ApiKeyId != null)
 0130    {
 0131      await ValidateApiKey((int)triggerUpdateBusinessModel.ApiKeyId);
 0132    }
 133
 0134    return await _context.Triggers
 0135      .Where(t => t.Id == triggerId)
 0136      .ExecuteUpdateAsync(setters => setters
 0137        .SetProperty(t => t.Name, triggerUpdateBusinessModel.Name)
 0138        .SetProperty(t => t.Url, triggerUpdateBusinessModel.Url)
 0139        .SetProperty(t => t.HttpMethodId, triggerUpdateBusinessModel.HttpMethodId)
 0140        .SetProperty(t => t.Body, triggerUpdateBusinessModel.Body)
 0141        .SetProperty(t => t.ApiKeyInsertLocationId, triggerUpdateBusinessModel.ApiKeyInsertLocationId)
 0142        .SetProperty(t => t.ApiKeyFieldName, triggerUpdateBusinessModel.ApiKeyFieldName)
 0143        .SetProperty(t => t.ApiKeyId, triggerUpdateBusinessModel.ApiKeyId)) == 1;
 0144  }
 145
 146  public async Task<bool> TestDeleteTriggerAsync(int triggerId)
 2147  {
 2148    var depeendencies = await _context.FlowDetails.Where(t => t.TriggerId == triggerId).CountAsync();
 2149    if (depeendencies > 0)
 1150    {
 1151      throw new LgdxValidation400Expection(nameof(triggerId), $"This trigger has been used by {depeendencies} details in
 152    }
 1153    return true;
 1154  }
 155
 156  public async Task<bool> DeleteTriggerAsync(int triggerId)
 0157  {
 0158    return await _context.Triggers.Where(t => t.Id == triggerId)
 0159      .ExecuteDeleteAsync() == 1;
 0160  }
 161
 162  public async Task<IEnumerable<TriggerSearchBusinessModel>> SearchTriggersAsync(string? name)
 4163  {
 4164    var n = name ?? string.Empty;
 4165    return await _context.Triggers.AsNoTracking()
 4166      .Where(w => w.Name.ToLower().Contains(n.ToLower()))
 4167      .Take(10)
 4168      .Select(t => new TriggerSearchBusinessModel {
 4169        Id = t.Id,
 4170        Name = t.Name,
 4171      })
 4172      .ToListAsync();
 4173  }
 174
 175  private string GetRobotName(Guid robotId)
 3176  {
 3177    return _context.Robots.AsNoTracking().Where(r => r.Id == robotId).FirstOrDefault()?.Name ?? string.Empty;
 3178  }
 179
 180  private string GetRealmName(int realmId)
 3181  {
 3182    return _context.Realms.AsNoTracking().Where(r => r.Id == realmId).FirstOrDefault()?.Name ?? string.Empty;
 3183  }
 184
 185  private string GeneratePresetValue(int i, AutoTask autoTask)
 8186  {
 8187    return i switch
 8188    {
 1189      (int)TriggerPresetValue.AutoTaskId => $"{autoTask.Id}",
 1190      (int)TriggerPresetValue.AutoTaskName => $"{autoTask.Name}",
 1191      (int)TriggerPresetValue.AutoTaskCurrentProgressId => $"{autoTask.CurrentProgressId}",
 1192      (int)TriggerPresetValue.AutoTaskCurrentProgressName => $"{autoTask.CurrentProgress.Name!}",
 1193      (int)TriggerPresetValue.RobotId => $"{autoTask.AssignedRobotId}",
 1194      (int)TriggerPresetValue.RobotName => $"{GetRobotName((Guid)autoTask.AssignedRobotId!)}",
 1195      (int)TriggerPresetValue.RealmId => $"{autoTask.RealmId}",
 1196      (int)TriggerPresetValue.RealmName => $"{GetRealmName(autoTask.RealmId)}",
 0197      _ => string.Empty,
 8198    };
 8199  }
 200
 201  public async Task InitialiseTriggerAsync(AutoTask autoTask, FlowDetail flowDetail)
 2202  {
 2203    var trigger = await _context.Triggers.AsNoTracking()
 2204      .Where(t => t.Id == flowDetail.TriggerId)
 2205      .FirstOrDefaultAsync();
 2206    if (trigger == null)
 1207    {
 1208      return;
 209    }
 210
 1211    var bodyDictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(trigger.Body ?? "{}");
 1212    if (bodyDictionary != null)
 1213    {
 214      // Replace Preset Value
 19215      foreach (var pair in bodyDictionary)
 8216      {
 8217        if (pair.Value.Length >= 5) // ((1)) has 5 characters
 8218        {
 8219          if (int.TryParse(pair.Value[2..^2], out int p))
 8220          {
 8221            bodyDictionary[pair.Key] = GeneratePresetValue(p, autoTask);
 8222          }
 8223        }
 8224      }
 225      // Add Next Token
 1226      if (flowDetail.AutoTaskNextControllerId != (int) AutoTaskNextController.Robot && autoTask.NextToken != null)
 1227      {
 1228        bodyDictionary.Add("NextToken", autoTask.NextToken);
 1229      }
 230
 1231      await _bus.Publish(new AutoTaskTriggerContract {
 1232        Trigger = trigger,
 1233        Body = bodyDictionary,
 1234        AutoTaskId = autoTask.Id,
 1235        AutoTaskName = autoTask.Name!,
 1236        RobotId = (Guid)autoTask.AssignedRobotId!,
 1237        RobotName = GetRobotName((Guid)autoTask.AssignedRobotId!),
 1238        RealmId = autoTask.RealmId,
 1239        RealmName = GetRealmName(autoTask.RealmId),
 1240      });
 1241    }
 2242  }
 243
 244  public async Task<bool> RetryTriggerAsync(AutoTask autoTask, Trigger trigger, string body)
 1245  {
 1246    var bodyDictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(body ?? "{}");
 1247    if (bodyDictionary != null)
 1248    {
 1249      await _bus.Publish(new AutoTaskTriggerContract {
 1250        Trigger = trigger,
 1251        Body = bodyDictionary,
 1252        AutoTaskId = autoTask.Id,
 1253        AutoTaskName = autoTask.Name!,
 1254        RobotId = (Guid)autoTask.AssignedRobotId!,
 1255        RobotName = GetRobotName((Guid)autoTask.AssignedRobotId!),
 1256        RealmId = autoTask.RealmId,
 1257        RealmName = GetRealmName(autoTask.RealmId),
 1258      });
 1259      return true;
 260    }
 0261    return false;
 1262  }
 263}