using CMSDAL.DTO.DataSource; using CMSDAL.Query.DataSource; using GB5Shared.DTO.Framework.Login; using GB5Shared.QueryExecutor; using GB5Shared.Resource.Response; using GB5Shared.Validation; using Newtonsoft.Json; using System.Data.Common; using System.Linq; namespace CMSDAL.CustomCode.DataSource; public class DataSourceDAL : IDataSourceDAL { private readonly IQueryExecutor _qe; private readonly IValidation _validation; public DataSourceDAL(IQueryExecutor qe, IValidation validation) { _qe = qe; _validation = validation; } public async Task GetListAsync(LoginDTO loginDTO, CancellationToken ct) { try { var result = await _qe.QueryAsync(loginDTO, DataSourceQB.GET_LIST, new { TenantId = loginDTO.ClientId }, cancellationToken: ct); return JsonConvert.SerializeObject(result); } catch { throw; } } public async Task GetByIdAsync(int dataSourceId, LoginDTO loginDTO, CancellationToken ct) { try { var result = await _qe.QueryAsync(loginDTO, DataSourceQB.GET_BY_ID, new { DataSourceId = dataSourceId, TenantId = loginDTO.ClientId }, cancellationToken: ct); return JsonConvert.SerializeObject(result.FirstOrDefault()); } catch { throw; } } public async Task GetByCodeAsync(string dataSourceCode, LoginDTO loginDTO, CancellationToken ct) { try { var result = await _qe.QueryAsync(loginDTO, DataSourceQB.GET_BY_CODE, new { DataSourceCode = dataSourceCode, TenantId = loginDTO.ClientId }, cancellationToken: ct); return JsonConvert.SerializeObject(result.FirstOrDefault()); } catch { throw; } } public async Task SaveAsync(DataSourceDTO dto, LoginDTO loginDTO, DbTransaction? tx, CancellationToken ct) { try { // TenantId bound from loginDTO.ClientId, never dto.TenantId directly — a hidden // form field (or any other client-controlled payload) must never be able to write // an arbitrary tenant into a new row. DataSourceBLL.SaveDataSource already // overrides dto.TenantId to match before calling this, but binding it explicitly // here too means this DAL method is safe on its own, not just by caller discipline. int newId = await _qe.ExecuteIdentityAsync(loginDTO, DataSourceQB.SAVE_DATASOURCE, new { dto.DataSourceCode, dto.DataSourceName, dto.DataSourceDescription, dto.EndpointTemplate, dto.HttpMethod, dto.AuthMode, dto.AuthHeaderName, dto.VaultSecretPath, dto.CacheTtlSeconds, dto.ResponseMappingJson, dto.Status, TenantId = loginDTO.ClientId, dto.CreatedById, dto.CreatedOn, dto.ModifiedById, dto.ModifiedOn }, tx); dto.DataSourceId = newId; dto.TenantId = loginDTO.ClientId; return newId; } catch (Exception ex) { string error = await _validation.HandleException(ex, $"{ErrorResponse.SaveErrorMessage}"); throw new Exception(error); } } public async Task UpdateAsync(DataSourceDTO dto, LoginDTO loginDTO, DbTransaction? tx, CancellationToken ct) { try { // Same reasoning as SaveAsync above — TenantId bound from loginDTO.ClientId, never // trusted from dto directly, both for the WHERE clause's tenant filter and to stop // a stray client-submitted TenantId from silently no-op'ing the update (WHERE // TENANTID = @TenantId would otherwise match zero rows if a hidden field carried // the wrong value). await _qe.ExecuteAsync(loginDTO, DataSourceQB.UPDATE_DATASOURCE, new { dto.DataSourceId, dto.DataSourceName, dto.DataSourceDescription, dto.EndpointTemplate, dto.HttpMethod, dto.AuthMode, dto.AuthHeaderName, dto.VaultSecretPath, dto.CacheTtlSeconds, dto.ResponseMappingJson, dto.Status, TenantId = loginDTO.ClientId, dto.ModifiedById, dto.ModifiedOn }, tx, ct); dto.TenantId = loginDTO.ClientId; return dto.DataSourceId; } catch (Exception ex) { string error = await _validation.HandleException(ex, $"{ErrorResponse.UpdateErrorMessage}"); throw new Exception(error); } } }