using GB5Shared.DTO.Framework.Login; using GB5Shared.QueryExecutor; using IceImportDAL.DTO.GcmCodeLookup; using IceImportDAL.Query.GcmCodeLookup; namespace IceImportDAL.CustomCode.GcmCodeLookup; // Mirrors GB5Shared/Addon/AddonService.cs's _allowedTables whitelist discipline exactly: // table names are never taken from uploaded file content, only from server-validated config // (IceMapDetails.GcmTypeId), and are checked against a fixed whitelist before any query runs. public class GcmCodeLookupDAL : IGcmCodeLookupDAL { private const int ChunkSize = 2000; // legacy's own safe IN-clause batch size, per the plan // MGCM is currently the only registered generic code/define table. Extend this set (never // bypass it) if a future genuinely-uniform lookup table needs the same generic dispatch. private static readonly HashSet _allowedTables = new(StringComparer.OrdinalIgnoreCase) { "MGCM" }; private readonly IQueryExecutor _qe; public GcmCodeLookupDAL(IQueryExecutor qe) => _qe = qe; public Task> ResolveCodesToIdsAsync( int gcmTypeId, IEnumerable codes, LoginDTO login, CancellationToken ct) => ResolveAsync(gcmTypeId, codes, GcmCodeLookupQB.RESOLVE_CODES_BY_GCMTYPE, login, ct); public Task> ResolveNamesToIdsAsync( int gcmTypeId, IEnumerable names, LoginDTO login, CancellationToken ct) => ResolveAsync(gcmTypeId, names, GcmCodeLookupQB.RESOLVE_NAMES_BY_GCMTYPE, login, ct); private async Task> ResolveAsync( int gcmTypeId, IEnumerable values, string sql, LoginDTO login, CancellationToken ct) { if (!_allowedTables.Contains("MGCM")) throw new InvalidOperationException("MGCM is not a registered lookup table."); var distinct = (values ?? Enumerable.Empty()) .Where(v => !string.IsNullOrWhiteSpace(v)) .Select(v => v.Trim()) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); var result = new Dictionary(StringComparer.OrdinalIgnoreCase); if (distinct.Count == 0) return result; for (int offset = 0; offset < distinct.Count; offset += ChunkSize) { var chunk = distinct.Skip(offset).Take(ChunkSize).ToList(); var rows = await _qe.QueryAsync( login, sql, new { GcmTypeId = gcmTypeId, Values = chunk }, cancellationToken: ct) .ConfigureAwait(false); foreach (var row in rows) result[row.Value] = row.Id; } return result; } }