// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using EpicGames.Horde.Issues; namespace EpicGames.Horde.Jobs.Graphs { /// /// Interface which wraps a generic key/value dictionary to provide specific node annotations /// #pragma warning disable CA1710 // Identifiers should have correct suffix public interface IReadOnlyNodeAnnotations : IReadOnlyDictionary #pragma warning restore CA1710 // Identifiers should have correct suffix { /// /// Workflow to use for triaging issues from this node /// WorkflowId? WorkflowId { get; } /// /// Whether to create issues for this node /// bool? CreateIssues { get; } /// /// Whether to automatically assign issues that could only be caused by one user, or have a well defined correlation with a modified file. /// bool? AutoAssign { get; } /// /// Automatically assign any issues to the given user /// string? AutoAssignToUser { get; } /// /// Whether to notify all submitters between a build suceeding and failing, allowing them to step forward and take ownership of an issue. /// bool? NotifySubmitters { get; } /// /// Key to use for grouping issues together, preventing them being merged with other groups /// string? IssueGroup { get; } /// /// Whether failures in this node should be flagged as build blockers /// bool? BuildBlocker { get; } } /// /// Set of annotations for a node /// public class NodeAnnotations : Dictionary, IReadOnlyNodeAnnotations { /// /// Empty annotation dictionary /// public static IReadOnlyNodeAnnotations Empty { get; } = new NodeAnnotations(); /// public const string WorkflowKeyName = "Workflow"; /// public const string CreateIssuesKeyName = "CreateIssues"; /// public const string AutoAssignKeyName = "AutoAssign"; /// public const string AutoAssignToUserKeyName = "AutoAssignToUser"; /// public const string NotifySubmittersKeyName = "NotifySubmitters"; /// public const string IssueGroupKeyName = "IssueGroup"; /// public const string BuildBlockerKeyName = "BuildBlocker"; /// /// Constructor /// public NodeAnnotations() : base(StringComparer.OrdinalIgnoreCase) { } /// /// Constructor /// /// public NodeAnnotations(IReadOnlyDictionary annotations) : this() { Merge(annotations); } /// public WorkflowId? WorkflowId { get { string? workflowName; if (!TryGetValue(WorkflowKeyName, out workflowName)) { return null; } try { return new WorkflowId(workflowName); } catch { return null; } } set => SetStringValue(WorkflowKeyName, value?.ToString()); } /// public bool? CreateIssues { get => GetBoolValue(CreateIssuesKeyName); set => SetBoolValue(CreateIssuesKeyName, value); } /// public bool? AutoAssign { get => GetBoolValue(AutoAssignKeyName); set => SetBoolValue(AutoAssignKeyName, value); } /// public string? AutoAssignToUser { get => GetStringValue(AutoAssignToUserKeyName); set => SetStringValue(AutoAssignToUserKeyName, value); } /// public bool? NotifySubmitters { get => GetBoolValue(NotifySubmittersKeyName); set => SetBoolValue(NotifySubmittersKeyName, value); } /// public string? IssueGroup { get => GetStringValue(IssueGroupKeyName); set => SetStringValue(IssueGroupKeyName, value); } /// public bool? BuildBlocker { get => GetBoolValue(BuildBlockerKeyName); set => SetBoolValue(BuildBlockerKeyName, value); } private bool? GetBoolValue(string key) { string? value = GetStringValue(key); if (value != null) { if (value.Equals("0", StringComparison.Ordinal) || value.Equals("false", StringComparison.OrdinalIgnoreCase)) { return false; } if (value.Equals("1", StringComparison.Ordinal) || value.Equals("true", StringComparison.OrdinalIgnoreCase)) { return true; } } return null; } private void SetBoolValue(string key, bool? value) { if (value == null) { Remove(key); } else { SetStringValue(key, value.Value ? "1" : "0"); } } private string? GetStringValue(string key) { TryGetValue(key, out string? value); return value; } private void SetStringValue(string key, string? value) { if (value == null) { Remove(key); } else { this[key] = value; } } /// /// Merge in entries from another set of annotation /// /// public void Merge(IReadOnlyDictionary other) { foreach ((string key, string value) in other) { this[key] = value; } } } }