using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using ScadaLink.Commons.Entities.InboundApi; using ScadaLink.Commons.Interfaces.Repositories; namespace ScadaLink.ConfigurationDatabase.Repositories; public class InboundApiRepository : IInboundApiRepository { private readonly ScadaLinkDbContext _context; private readonly ILogger _logger; public InboundApiRepository(ScadaLinkDbContext context, ILogger? logger = null) { _context = context ?? throw new ArgumentNullException(nameof(context)); _logger = logger ?? NullLogger.Instance; } public async Task GetApiKeyByIdAsync(int id, CancellationToken cancellationToken = default) => await _context.Set().FindAsync(new object[] { id }, cancellationToken); public async Task> GetAllApiKeysAsync(CancellationToken cancellationToken = default) => await _context.Set().ToListAsync(cancellationToken); public async Task GetApiKeyByValueAsync(string keyValue, CancellationToken cancellationToken = default) => await _context.Set().FirstOrDefaultAsync(k => k.KeyValue == keyValue, cancellationToken); public async Task AddApiKeyAsync(ApiKey apiKey, CancellationToken cancellationToken = default) => await _context.Set().AddAsync(apiKey, cancellationToken); public Task UpdateApiKeyAsync(ApiKey apiKey, CancellationToken cancellationToken = default) { _context.Set().Update(apiKey); return Task.CompletedTask; } public async Task DeleteApiKeyAsync(int id, CancellationToken cancellationToken = default) { var entity = await GetApiKeyByIdAsync(id, cancellationToken); if (entity != null) _context.Set().Remove(entity); } public async Task GetApiMethodByIdAsync(int id, CancellationToken cancellationToken = default) => await _context.Set().FindAsync(new object[] { id }, cancellationToken); public async Task> GetAllApiMethodsAsync(CancellationToken cancellationToken = default) => await _context.Set().ToListAsync(cancellationToken); public async Task GetMethodByNameAsync(string name, CancellationToken cancellationToken = default) => await _context.Set().FirstOrDefaultAsync(m => m.Name == name, cancellationToken); public async Task> GetApprovedKeysForMethodAsync(int methodId, CancellationToken cancellationToken = default) { var method = await _context.Set().FindAsync(new object[] { methodId }, cancellationToken); if (method?.ApprovedApiKeyIds == null) return new List(); // ApprovedApiKeyIds is a comma-separated string of integer ApiKey ids. A token that // fails to parse indicates a corrupt value: it is dropped (it cannot identify a key), // but the corruption is logged as a warning so it is observable rather than silent. // A corrupt list would otherwise quietly approve fewer keys than intended. var keyIds = new List(); foreach (var token in method.ApprovedApiKeyIds.Split(',', StringSplitOptions.RemoveEmptyEntries)) { var trimmed = token.Trim(); if (int.TryParse(trimmed, out var id) && id > 0) { keyIds.Add(id); } else { _logger.LogWarning( "ApiMethod {MethodId} has a malformed approved-API-key id token '{Token}' " + "in ApprovedApiKeyIds; it was dropped. The method may approve fewer keys than expected.", methodId, trimmed); } } return await _context.Set().Where(k => keyIds.Contains(k.Id)).ToListAsync(cancellationToken); } public async Task AddApiMethodAsync(ApiMethod method, CancellationToken cancellationToken = default) => await _context.Set().AddAsync(method, cancellationToken); public Task UpdateApiMethodAsync(ApiMethod method, CancellationToken cancellationToken = default) { _context.Set().Update(method); return Task.CompletedTask; } public async Task DeleteApiMethodAsync(int id, CancellationToken cancellationToken = default) { var entity = await GetApiMethodByIdAsync(id, cancellationToken); if (entity != null) _context.Set().Remove(entity); } public async Task SaveChangesAsync(CancellationToken cancellationToken = default) => await _context.SaveChangesAsync(cancellationToken); }