diff --git a/src/ApiService/ApiService/Functions/Notifications.cs b/src/ApiService/ApiService/Functions/Notifications.cs index 205dabe4d2..106396646e 100644 --- a/src/ApiService/ApiService/Functions/Notifications.cs +++ b/src/ApiService/ApiService/Functions/Notifications.cs @@ -22,7 +22,7 @@ private async Async.Task Get(HttpRequestData req) { return await _context.RequestHandling.NotOk(req, request.ErrorV, "notification search"); } - var entries = request.OkV switch { { Container: null } => _context.NotificationOperations.SearchAll(), { Container: var c } => _context.NotificationOperations.SearchByRowKeys(c.Select(x => x.String)) + var entries = request.OkV switch { { Container: null, NotificationId: null } => _context.NotificationOperations.SearchAll(), { Container: var c, NotificationId: null } => _context.NotificationOperations.SearchByRowKeys(c.Select(x => x.String)), { Container: var _, NotificationId: var n } => new[] { await _context.NotificationOperations.GetNotification(n.Value) }.ToAsyncEnumerable(), }; var response = req.CreateResponse(HttpStatusCode.OK); diff --git a/src/ApiService/ApiService/OneFuzzTypes/Requests.cs b/src/ApiService/ApiService/OneFuzzTypes/Requests.cs index 062abecced..baabf8bbd2 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Requests.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Requests.cs @@ -125,7 +125,8 @@ public record NotificationCreate( ) : BaseRequest; public record NotificationSearch( - List? Container + List? Container, + Guid? NotificationId ) : BaseRequest; public record NotificationGet( diff --git a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs index 79a918a2a4..14ef941e4a 100644 --- a/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/NotificationOperations.cs @@ -9,6 +9,7 @@ public interface INotificationOperations : IOrm { IAsyncEnumerable GetNotifications(Container container); IAsyncEnumerable<(Task, IEnumerable)> GetQueueTasks(); Async.Task> Create(Container container, NotificationTemplate config, bool replaceExisting); + Async.Task GetNotification(Guid notifificationId); } public class NotificationOperations : Orm, INotificationOperations { @@ -142,4 +143,8 @@ private async Async.Task HideSecrets(NotificationTemplate _logTracer.Error($"unable to find crash_report or no repro entry for report: {JsonSerializer.Serialize(report)}"); return null; } + + public async Async.Task GetNotification(Guid notifificationId) { + return await SearchByPartitionKeys(new[] { notifificationId.ToString() }).SingleAsync(); + } } diff --git a/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs b/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs index 5ded817f51..abd54377ed 100644 --- a/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs +++ b/src/ApiService/ApiService/onefuzzlib/notifications/Ado.cs @@ -33,6 +33,11 @@ public async Async.Task NotifyAdo(AdoTemplate config, Container container, strin await ado.Process(notificationInfo); } catch (Exception e) when (e is VssAuthenticationException || e is VssServiceException) { + var _ = config.AdoFields.TryGetValue("System.AssignedTo", out var assignedTo); + if (e is VssAuthenticationException && !string.IsNullOrEmpty(assignedTo)) { + notificationInfo = notificationInfo.AddRange(new (string, string)[] { ("assigned_to", assignedTo) }); + } + if (!isLastRetryAttempt && IsTransient(e)) { _logTracer.WithTags(notificationInfo).Error($"transient ADO notification failure {report.JobId:Tag:JobId} {report.TaskId:Tag:TaskId} {container:Tag:Container} {filename:Tag:Filename}"); throw;