< Summary

Information
Class: LGDXRobotCloud.API.Services.Administration.ApiKeyService
Assembly: LGDXRobotCloud.API
File(s): /builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Administration/ApiKeyService.cs
Line coverage
77%
Covered lines: 88
Uncovered lines: 26
Coverable lines: 114
Total lines: 167
Line coverage: 77.1%
Branch coverage
70%
Covered branches: 17
Total branches: 24
Branch coverage: 70.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%44100%
GetApiKeysAsync()100%22100%
GetApiKeyAsync()100%22100%
GetApiKeySecretAsync()100%22100%
GenerateApiKeys()100%11100%
AddApiKeyAsync()100%22100%
UpdateApiKeyAsync()100%210%
UpdateApiKeySecretAsync()100%66100%
DeleteApiKeyAsync()100%210%
SearchApiKeysAsync()50%22100%
ValidateApiKeyAsync()0%2040%

File(s)

/builds/yukaitung/lgdxrobot2-cloud/LGDXRobotCloud.API/Services/Administration/ApiKeyService.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2using LGDXRobotCloud.API.Exceptions;
 3using LGDXRobotCloud.Data.DbContexts;
 4using LGDXRobotCloud.Data.Entities;
 5using LGDXRobotCloud.Data.Models.Business.Administration;
 6using LGDXRobotCloud.Utilities.Helpers;
 7using Microsoft.EntityFrameworkCore;
 8using Microsoft.Extensions.Caching.Memory;
 9
 10namespace LGDXRobotCloud.API.Services.Administration;
 11
 12public interface IApiKeyService
 13{
 14  Task<(IEnumerable<ApiKeyBusinessModel>, PaginationHelper)> GetApiKeysAsync(string? name, bool isThirdParty, int pageNu
 15  Task<ApiKeyBusinessModel> GetApiKeyAsync(int apiKeyId);
 16  Task<ApiKeySecretBusinessModel> GetApiKeySecretAsync(int apiKeyId);
 17  Task<ApiKeyBusinessModel> AddApiKeyAsync(ApiKeyCreateBusinessModel apiKeyCreateBusinessModel);
 18  Task<bool> UpdateApiKeyAsync(int apiKeyId, ApiKeyUpdateBusinessModel apiKeyUpdateBusinessModel);
 19  Task<bool> UpdateApiKeySecretAsync(int apiKeyId, ApiKeySecretUpdateBusinessModel apiKeySecretUpdateBusinessModel);
 20  Task<bool> DeleteApiKeyAsync(int apiKeyId);
 21
 22  Task<IEnumerable<ApiKeySearchBusinessModel>> SearchApiKeysAsync(string? name);
 23  Task<bool> ValidateApiKeyAsync(string apiKey);
 24}
 25
 2726public class ApiKeyService(
 2727    IMemoryCache memoryCache,
 2728    LgdxContext context
 2729  ) : IApiKeyService
 30{
 2731  private readonly LgdxContext _context = context ?? throw new ArgumentNullException(nameof(context));
 2732  private readonly IMemoryCache _memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
 33
 34  public async Task<(IEnumerable<ApiKeyBusinessModel>, PaginationHelper)>GetApiKeysAsync(string? name, bool isThirdParty
 635  {
 636    var query = _context.ApiKeys as IQueryable<ApiKey>;
 637    query = query.Where(a => a.IsThirdParty == isThirdParty);
 638    if(!string.IsNullOrWhiteSpace(name))
 339    {
 340      name = name.Trim();
 341      query = query.Where(a => a.Name.ToLower().Contains(name.ToLower()));
 342    }
 643    var itemCount = await query.CountAsync();
 644    var PaginationHelper = new PaginationHelper(itemCount, pageNumber, pageSize);
 645    var apiKeys = await query.AsNoTracking()
 646      .OrderBy(a => a.Id)
 647      .Skip(pageSize * (pageNumber - 1))
 648      .Take(pageSize)
 649      .Select(a => new ApiKeyBusinessModel {
 650        Id = a.Id,
 651        Name = a.Name,
 652        IsThirdParty = a.IsThirdParty,
 653      })
 654      .ToListAsync();
 655    return (apiKeys, PaginationHelper);
 656  }
 57
 58  public async Task<ApiKeyBusinessModel> GetApiKeyAsync(int apiKeyId)
 559  {
 560    return await _context.ApiKeys.AsNoTracking()
 561      .Where(a => a.Id == apiKeyId)
 562      .Select(a => new ApiKeyBusinessModel {
 563        Id = a.Id,
 564        Name = a.Name,
 565        IsThirdParty = a.IsThirdParty,
 566      })
 567      .FirstOrDefaultAsync()
 568        ?? throw new LgdxNotFound404Exception();
 469  }
 70
 71  public async Task<ApiKeySecretBusinessModel> GetApiKeySecretAsync(int apiKeyId)
 572  {
 573    return await _context.ApiKeys.AsNoTracking()
 574      .Where(a => a.Id == apiKeyId)
 575      .Select(a => new ApiKeySecretBusinessModel {
 576        Secret = a.Secret,
 577      })
 578      .FirstOrDefaultAsync()
 579        ?? throw new LgdxNotFound404Exception();
 480  }
 81
 82  private static string GenerateApiKeys()
 183  {
 184    var bytes = RandomNumberGenerator.GetBytes(32);
 185    string base64String = Convert.ToBase64String(bytes)
 186      .Replace("+", "-")
 187      .Replace("/", "_");
 188    return "LGDX" + base64String;
 189  }
 90
 91  public async Task<ApiKeyBusinessModel> AddApiKeyAsync(ApiKeyCreateBusinessModel apiKeyCreateBusinessModel)
 292  {
 293    if (!apiKeyCreateBusinessModel.IsThirdParty)
 194      apiKeyCreateBusinessModel.Secret = GenerateApiKeys();
 295    var apikey = apiKeyCreateBusinessModel.ToEntity();
 296    await _context.ApiKeys.AddAsync(apikey);
 297    await _context.SaveChangesAsync();
 298    return new ApiKeyBusinessModel{
 299      Id = apikey.Id,
 2100      Name = apikey.Name,
 2101      IsThirdParty = apikey.IsThirdParty
 2102    };
 2103  }
 104
 105  public async Task<bool> UpdateApiKeyAsync(int apiKeyId, ApiKeyUpdateBusinessModel apiKeyUpdateBusinessModel)
 0106  {
 0107    return await _context.ApiKeys
 0108      .Where(a => a.Id == apiKeyId)
 0109      .ExecuteUpdateAsync(setters => setters
 0110        .SetProperty(a => a.Name, apiKeyUpdateBusinessModel.Name)) == 1;
 0111  }
 112
 113  public async Task<bool> UpdateApiKeySecretAsync(int apiKeyId, ApiKeySecretUpdateBusinessModel apiKeySecretUpdateBusine
 4114  {
 4115    var apiKey = await _context.ApiKeys.Where(a => a.Id == apiKeyId).FirstOrDefaultAsync()
 4116      ?? throw new LgdxNotFound404Exception();
 3117    if (!apiKey.IsThirdParty && !string.IsNullOrEmpty(apiKeySecretUpdateBusinessModel.Secret))
 1118    {
 1119      throw new LgdxValidation400Expection(nameof(apiKeySecretUpdateBusinessModel.Secret), "The LGDXRobot Cloud API Key 
 120    }
 121
 2122    apiKey.Secret = apiKeySecretUpdateBusinessModel.Secret;
 2123    return await _context.SaveChangesAsync() == 1;
 2124  }
 125
 126  public async Task<bool> DeleteApiKeyAsync(int apiKeyId)
 0127  {
 0128    return await _context.ApiKeys.Where(a => a.Id == apiKeyId)
 0129      .ExecuteDeleteAsync() == 1;
 0130  }
 131
 132  public async Task<IEnumerable<ApiKeySearchBusinessModel>> SearchApiKeysAsync(string? name)
 5133  {
 5134    var n = name ?? string.Empty;
 5135    return await _context.ApiKeys.AsNoTracking()
 5136      .Where(w => w.Name.ToLower().Contains(n.ToLower()))
 5137      .Where(w => w.IsThirdParty == true)
 5138      .Take(10)
 5139      .Select(t => new ApiKeySearchBusinessModel {
 5140        Id = t.Id,
 5141        Name = t.Name,
 5142      })
 5143      .ToListAsync();
 5144  }
 145
 146  public async Task<bool> ValidateApiKeyAsync(string apiKey)
 0147  {
 0148    string hashed = LgdxHelper.GenerateSha256Hash(apiKey);
 0149    _memoryCache.TryGetValue($"ValidateApiKeyAsync_{hashed}", out bool? exist);
 0150    if (exist != null)
 0151    {
 0152      return (bool)exist;
 153    }
 154
 0155    var result = await _context.ApiKeys.AsNoTracking()
 0156      .Where(a => a.Secret == apiKey)
 0157      .Where(a => !a.IsThirdParty)
 0158      .AnyAsync();
 159
 0160    if (result)
 0161    {
 0162      _memoryCache.Set($"ValidateApiKeyAsync_{hashed}", true, TimeSpan.FromMinutes(5));
 0163    }
 164
 0165    return result;
 0166  }
 167}