// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using EpicGames.Core;
using EpicGames.Horde.Agents.Pools;
using EpicGames.Horde.Issues;
using EpicGames.Horde.Jobs;
using EpicGames.Horde.Jobs.Schedules;
using EpicGames.Horde.Jobs.Templates;
using EpicGames.Horde.Projects;
using EpicGames.Horde.Users;
#pragma warning disable CA2227 // Change x to be read-only by removing the property setter
namespace EpicGames.Horde.Streams
{
///
/// Response describing a stream
///
public class GetStreamResponse
{
///
/// Unique id of the stream
///
public StreamId Id { get; set; }
///
/// Unique id of the project containing this stream
///
public ProjectId ProjectId { get; set; }
///
/// Name of the stream
///
public string Name { get; set; }
///
/// The config file path on the server
///
public string ConfigPath { get; set; } = String.Empty;
///
/// Revision of the config file
///
public string ConfigRevision { get; set; } = String.Empty;
///
/// Order to display in the list
///
public int Order { get; set; }
///
/// Notification channel for all jobs in this stream
///
public string? NotificationChannel { get; set; }
///
/// Notification channel filter for this template. Can be a combination of "Success", "Failure" and "Warnings" separated by pipe characters.
///
public string? NotificationChannelFilter { get; set; }
///
/// Channel to post issue triage notifications
///
public string? TriageChannel { get; set; }
///
/// Default template for running preflights
///
public string? DefaultPreflightTemplate { get; set; }
///
/// Default template to use for preflights
///
public DefaultPreflightMessage? DefaultPreflight { get; set; }
///
/// List of tabs to display for this stream
///
public List Tabs { get; set; }
///
/// Map of agent name to type
///
public Dictionary AgentTypes { get; set; }
///
/// Map of workspace name to type
///
public Dictionary? WorkspaceTypes { get; set; }
///
/// Templates for jobs in this stream
///
public List Templates { get; set; }
///
/// Stream paused for new builds until this date
///
public DateTime? PausedUntil { get; set; }
///
/// Reason for stream being paused
///
public string? PauseComment { get; set; }
///
/// Workflows for this stream
///
public List Workflows { get; set; }
///
/// Constructor
///
/// The stream to construct from
/// Templates for this stream
public GetStreamResponse(IStream stream, List templates)
{
Id = stream.Id;
ProjectId = stream.ProjectId;
Name = stream.Name;
ConfigPath = stream.ConfigPath ?? String.Empty;
ConfigRevision = stream.ConfigRevision;
Order = stream.Order;
NotificationChannel = stream.NotificationChannel;
NotificationChannelFilter = stream.NotificationChannelFilter;
TriageChannel = stream.TriageChannel;
DefaultPreflightTemplate = stream.DefaultPreflight?.TemplateId?.ToString();
DefaultPreflight = (stream.DefaultPreflight == null) ? null : new DefaultPreflightMessage(stream.DefaultPreflight);
Tabs = stream.Tabs.ConvertAll(x => new GetStreamTabResponse(x));
AgentTypes = stream.AgentTypes.ToDictionary(x => x.Key, x => new GetAgentTypeResponse(x.Value));
WorkspaceTypes = stream.WorkspaceTypes.ToDictionary(x => x.Key, x => new GetWorkspaceTypeResponse(x.Value));
Templates = templates;
Workflows = stream.Workflows.ConvertAll(x => new GetWorkflowResponse(x));
PausedUntil = stream.PausedUntil;
PauseComment = stream.PauseComment;
}
}
///
/// Information about the default preflight to run
///
public class DefaultPreflightMessage : IDefaultPreflight
{
///
public TemplateId? TemplateId { get; set; }
///
public ChangeQueryMessage? Change { get; set; }
IChangeQuery? IDefaultPreflight.Change => Change;
///
/// Constructor
///
public DefaultPreflightMessage()
{ }
///
/// Constructor
///
public DefaultPreflightMessage(IDefaultPreflight other)
{
TemplateId = other.TemplateId;
Change = (other.Change == null) ? null : new ChangeQueryMessage(other.Change);
}
}
///
/// Information about a page to display in the dashboard for a stream
///
public class GetStreamTabResponse : IStreamTab
{
///
public string Title { get; set; }
///
public string Type { get; set; }
///
public TabStyle Style { get; set; }
///
public bool ShowNames { get; set; }
///
public bool? ShowPreflights { get; set; }
///
public List? JobNames { get; set; }
IReadOnlyList? IStreamTab.JobNames => JobNames;
///
public List? Templates { get; set; }
IReadOnlyList? IStreamTab.Templates => Templates;
///
public List? Columns { get; set; }
IReadOnlyList? IStreamTab.Columns => Columns;
///
/// Constructor
///
public GetStreamTabResponse()
{
Title = String.Empty;
Type = String.Empty;
}
///
/// Constructor
///
public GetStreamTabResponse(IStreamTab other)
{
Title = other.Title;
Type = other.Type;
Style = other.Style;
ShowNames = other.ShowNames;
ShowPreflights = other.ShowPreflights;
JobNames = other.JobNames?.ToList();
Templates = other.Templates?.ToList();
Columns = other.Columns?.ConvertAll(x => new GetStreamTabColumnResponse(x));
}
}
///
/// Describes a column to display on the jobs page
///
public class GetStreamTabColumnResponse : IStreamTabColumn
{
///
public TabColumnType Type { get; set; }
///
public string Heading { get; set; }
///
public string? Category { get; set; }
///
public string? Parameter { get; set; }
///
public int? RelativeWidth { get; set; }
///
/// Default constructor
///
public GetStreamTabColumnResponse()
{
Heading = String.Empty;
}
///
/// Constructor
///
public GetStreamTabColumnResponse(IStreamTabColumn other)
{
Type = other.Type;
Heading = other.Heading;
Category = other.Category;
Parameter = other.Parameter;
RelativeWidth = other.RelativeWidth;
}
}
///
/// Mapping from a BuildGraph agent type to a set of machines on the farm
///
public class GetAgentTypeResponse
{
///
/// Pool of agents to use for this agent type
///
public PoolId Pool { get; set; }
///
/// Name of the workspace to sync
///
public string? Workspace { get; set; }
///
/// Path to the temporary storage dir
///
public string? TempStorageDir { get; set; }
///
/// Environment variables to be set when executing the job
///
public Dictionary? Environment { get; set; }
///
/// Constructor
///
public GetAgentTypeResponse()
{ }
///
/// Constructor
///
public GetAgentTypeResponse(IAgentType other)
{
Pool = other.Pool;
Workspace = other.Workspace;
TempStorageDir = other.TempStorageDir;
Environment = other.Environment?.ToDictionary();
}
}
///
/// Information about a workspace type
///
public class GetWorkspaceTypeResponse : IWorkspaceType
{
///
public string? Cluster { get; set; }
///
public string? ServerAndPort { get; set; }
///
public string? UserName { get; set; }
///
string? IWorkspaceType.Password => throw new NotSupportedException();
///
public string? Identifier { get; set; }
///
public string? Stream { get; set; }
///
public List? View { get; set; }
IReadOnlyList? IWorkspaceType.View => View;
///
public bool? Incremental { get; set; }
///
public bool? UseAutoSdk { get; set; }
///
public List? AutoSdkView { get; set; }
IReadOnlyList? IWorkspaceType.AutoSdkView => AutoSdkView;
///
public string? Method { get; set; } = null;
///
public long? MinScratchSpace { get; set; } = null;
///
public long? ConformDiskFreeSpace { get; set; } = null;
///
/// Default constructor
///
public GetWorkspaceTypeResponse()
{ }
///
/// Constructor
///
public GetWorkspaceTypeResponse(IWorkspaceType other)
{
Cluster = other.Cluster;
ServerAndPort = other.ServerAndPort;
UserName = other.UserName;
Identifier = other.Identifier;
Stream = other.Stream;
View = other.View?.ToList();
Incremental = other.Incremental;
UseAutoSdk = other.UseAutoSdk;
AutoSdkView = other.AutoSdkView?.ToList();
Method = other.Method;
MinScratchSpace = other.MinScratchSpace;
ConformDiskFreeSpace = other.ConformDiskFreeSpace;
}
}
///
/// State information for a step in the stream
///
public class GetTemplateStepStateResponse
{
///
/// Name of the step
///
public string Name { get; set; } = String.Empty;
///
/// User who paused the step
///
public GetThinUserInfoResponse? PausedByUserInfo { get; set; }
///
/// The UTC time when the step was paused
///
public DateTime? PauseTimeUtc { get; set; }
///
/// Default constructor for serialization
///
private GetTemplateStepStateResponse()
{
}
///
/// Constructor
///
public GetTemplateStepStateResponse(ITemplateStep state, GetThinUserInfoResponse? pausedByUserInfo)
{
Name = state.Name;
PauseTimeUtc = state.PauseTimeUtc;
PausedByUserInfo = pausedByUserInfo;
}
}
///
/// Information about a template in this stream
///
public class GetTemplateRefResponse : GetTemplateResponseBase
{
///
/// Id of the template ref
///
public string Id { get; set; }
///
/// Hash of the template definition
///
public string Hash { get; set; }
///
/// Whether to show badges in UGS for these jobs
///
public bool ShowUgsBadges { get; set; }
///
/// Whether to show alerts in UGS for these jobs
///
public bool ShowUgsAlerts { get; set; }
///
/// Notification channel for this template. Overrides the stream channel if set.
///
public string? NotificationChannel { get; set; }
///
/// Notification channel filter for this template. Can be a combination of "Success", "Failure" and "Warnings" separated by pipe characters.
///
public string? NotificationChannelFilter { get; set; }
///
/// Triage channel for this template. Overrides the stream channel if set.
///
public string? TriageChannel { get; set; }
///
/// The schedule for this ref
///
public GetScheduleResponse? Schedule { get; set; }
///
/// List of templates to trigger
///
public List? ChainedJobs { get; set; }
///
/// List of step states
///
public List? StepStates { get; set; }
///
/// List of queries for the default changelist
///
public List? DefaultChange { get; set; }
///
/// Whether the user is allowed to create jobs from this template
///
public bool CanRun { get; set; }
///
/// Constructor
///
/// The template ref id
/// The template ref
/// The actual template
/// The template step states
/// The scheduler time zone
/// Whether the user can run this template
public GetTemplateRefResponse(TemplateId id, ITemplateRef templateRef, ITemplate template, List? stepStates, TimeZoneInfo schedulerTimeZone, bool canRun)
: base(template)
{
Id = id.ToString();
Hash = template.Hash.ToString();
ShowUgsBadges = templateRef.ShowUgsBadges;
ShowUgsAlerts = templateRef.ShowUgsAlerts;
NotificationChannel = templateRef.NotificationChannel;
NotificationChannelFilter = templateRef.NotificationChannelFilter;
Schedule = (templateRef.Schedule != null) ? new GetScheduleResponse(templateRef.Schedule, schedulerTimeZone) : null;
ChainedJobs = (templateRef.ChainedJobs != null && templateRef.ChainedJobs.Count > 0) ? templateRef.ChainedJobs.ConvertAll(x => new GetChainedJobTemplateResponse(x)) : null;
StepStates = stepStates;
DefaultChange = templateRef.DefaultChange?.ConvertAll(x => new ChangeQueryMessage(x));
CanRun = canRun;
}
}
///
/// Trigger for another template
///
public class GetChainedJobTemplateResponse : IChainedJobTemplate
{
///
public string Trigger { get; set; }
///
public TemplateId TemplateId { get; set; }
///
public bool UseDefaultChangeForTemplate { get; set; }
///
/// Constructor
///
public GetChainedJobTemplateResponse()
{
Trigger = String.Empty;
}
///
/// Constructor
///
public GetChainedJobTemplateResponse(IChainedJobTemplate other)
{
Trigger = other.Trigger;
TemplateId = other.TemplateId;
UseDefaultChangeForTemplate = other.UseDefaultChangeForTemplate;
}
}
///
/// Step state update request
///
public class UpdateStepStateRequest
{
///
/// Name of the step
///
[Required]
public string Name { get; set; } = String.Empty;
///
/// User who paused the step
///
public string? PausedByUserId { get; set; }
}
///
/// Updates an existing stream template ref
///
public class UpdateTemplateRefRequest
{
///
/// Step states to update
///
public List? StepStates { get; set; }
}
}