using System.Diagnostics; using GB5Shared.DTO.Framework.Login; using GB5Shared.QueryExecutor; using GB5Shared.Telemetry; using Microsoft.Extensions.Logging; using OpenTelemetry.Trace; namespace GB5Shared.Attachment { public interface IAttachmentResolveDAL { Task ResolveAsync(int objectHeaderTypeId, Guid headerRowGuid, int objectId, LoginDTO login, CancellationToken ct = default); } public sealed class AttachmentResolveDAL : IAttachmentResolveDAL { private readonly IQueryExecutor _queryExecutor; private readonly ILogger _logger; public AttachmentResolveDAL(IQueryExecutor queryExecutor, ILogger logger) { _queryExecutor = queryExecutor; _logger = logger; } public async Task ResolveAsync(int objectHeaderTypeId, Guid headerRowGuid, int objectId, LoginDTO login, CancellationToken ct = default) { // Dedicated, greppable span: search Zipkin for "SP_RESOLVE_ATTACHMENTS_BATCH" // to find every execution of this SP regardless of tenant/DB. using var activity = GB5ActivitySources.Database.StartActivity( "SP_RESOLVE_ATTACHMENTS_BATCH", ActivityKind.Client); activity?.SetTag("db.system", "mssql"); activity?.SetTag("db.name", login.DatabaseName); activity?.SetTag("gb5.connection.name", login.ConnectionDatabaseName ?? string.Empty); activity?.SetTag("gb5.object.header_type_id", objectHeaderTypeId); activity?.SetTag("gb5.object.id", objectId); activity?.SetTag("gb5.header_row_guid", headerRowGuid.ToString()); _logger.LogInformation( "[attachment-resolve] Calling SP_RESOLVE_ATTACHMENTS_BATCH | DB={DB} | ConnectionName={ConnectionName} | ObjectHeaderTypeId={ObjectHeaderTypeId} | HeaderRowGuid={HeaderRowGuid} | ObjectId={ObjectId}", login.DatabaseName, login.ConnectionDatabaseName, objectHeaderTypeId, headerRowGuid, objectId); try { await _queryExecutor.ExecuteAsync(login, AttachmentResolveQB.EXEC_RESOLVE_ATTACHMENTS, new { ObjectHeaderTypeId = objectHeaderTypeId, HeaderRowGuid = headerRowGuid // objectId is intentionally not sent: the deployed SP only declares // @ObjectHeaderTypeId/@HeaderRowGuid and resolves the header's real // PK itself from HeaderRowGuid. Passing a 3rd arg throws // "too many arguments specified" on every call. }); activity?.SetStatus(ActivityStatusCode.Ok); _logger.LogInformation( "[attachment-resolve] SP_RESOLVE_ATTACHMENTS_BATCH completed | DB={DB} | ObjectId={ObjectId} | HeaderRowGuid={HeaderRowGuid}", login.DatabaseName, objectId, headerRowGuid); } catch (Exception ex) { activity?.RecordException(ex); activity?.SetStatus(ActivityStatusCode.Error, ex.Message); _logger.LogError(ex, "[attachment-resolve] SP_RESOLVE_ATTACHMENTS_BATCH FAILED | DB={DB} | ConnectionName={ConnectionName} | ObjectHeaderTypeId={ObjectHeaderTypeId} | HeaderRowGuid={HeaderRowGuid} | ObjectId={ObjectId} | Error={Error}", login.DatabaseName, login.ConnectionDatabaseName, objectHeaderTypeId, headerRowGuid, objectId, ex.Message); throw; } } } }