// Copyright Epic Games, Inc. All Rights Reserved. using EpicGames.Core; using HordeServer.Api; using HordeServer.Utilities; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace HordeServer.Models { using ProjectId = StringId; /// /// Specifies a category of streams to display on the dashboard /// public class StreamCategory { /// /// Name of this group /// [BsonRequired] public string Name { get; set; } /// /// Row to display this category on /// public int Row { get; set; } /// /// Whether to show this category on the nav menu /// public bool ShowOnNavMenu { get; set; } = true; /// /// List of stream name patterns to include for this project /// public List IncludePatterns { get; set; } = new List(); /// /// Patterns for stream names to be excluded /// public List ExcludePatterns { get; set; } = new List(); /// /// Private constructor /// private StreamCategory() { Name = null!; } /// /// Constructor /// /// Name of this group public StreamCategory(string Name) { this.Name = Name; } /// /// Constructs a category from a request object /// /// The request object public StreamCategory(CreateProjectCategoryRequest Request) { this.Name = Request.Name; this.Row = Request.Row; this.ShowOnNavMenu = Request.ShowOnNavMenu; this.IncludePatterns = Request.IncludePatterns; this.ExcludePatterns = Request.ExcludePatterns; } /// /// Tests if a given name matches a pattern /// /// The name to test /// The pattern to match against /// True if the pattern matches, false otherwise public static bool MatchPattern(string Name, string Pattern) { return Regex.IsMatch(Name, Pattern); } } /// /// Represents a project /// public interface IProject { /// /// Identifier for the project. /// public ProjectId Id { get; } /// /// Name of the project /// public string Name { get; } /// /// Path to the config file used to configure this project /// public string? ConfigPath { get; } /// /// Revision of the config file used to configure this project /// public string? ConfigRevision { get; } /// /// Order to display on the dashboard /// public int Order { get; } /// /// The ACL for this object /// public Acl? Acl { get; } /// /// List of stream categories for this project. Controls how streams are displayed on the dashboard. /// public IReadOnlyList Categories { get; } } /// /// Interface for a document containing a project logo /// public interface IProjectLogo { /// /// The project id /// public ProjectId Id { get; } /// /// Path to the logo /// public string Path { get; } /// /// Revision of the file /// public string Revision { get; } /// /// Mime type for the image /// public string MimeType { get; } /// /// Image data /// public byte[] Data { get; } } /// /// Extension methods for projects /// public static class ProjectExtensions { /// /// Converts this object to a public response /// /// The project instance /// Whether to include streams in the response /// Whether to include categories in the response /// The list of streams /// Whether to include the ACL in the response /// Response instance public static GetProjectResponse ToResponse(this IProject Project, bool bIncludeStreams, bool bIncludeCategories, List? Streams, bool bIncludeAcl) { List? StreamResponses = null; if(bIncludeStreams) { StreamResponses = Streams!.ConvertAll(x => new GetProjectStreamResponse(x.Id.ToString(), x.Name)); } List? CategoryResponses = null; if (bIncludeCategories) { CategoryResponses = Project.Categories.ConvertAll(x => new GetProjectCategoryResponse(x)); if (Streams != null) { foreach (IStream Stream in Streams) { GetProjectCategoryResponse? CategoryResponse = CategoryResponses.FirstOrDefault(x => MatchCategory(Stream.Name, x)); if(CategoryResponse == null) { int Row = (CategoryResponses.Count > 0) ? CategoryResponses.Max(x => x.Row) : 0; if (CategoryResponses.Count(x => x.Row == Row) >= 3) { Row++; } StreamCategory OtherCategory = new StreamCategory("Other"); OtherCategory.Row = Row; OtherCategory.IncludePatterns.Add(".*"); CategoryResponse = new GetProjectCategoryResponse(OtherCategory); CategoryResponses.Add(CategoryResponse); } CategoryResponse.Streams!.Add(Stream.Id.ToString()); } } } GetAclResponse? AclResponse = (bIncludeAcl && Project.Acl != null) ? new GetAclResponse(Project.Acl) : null; return new GetProjectResponse(Project.Id.ToString(), Project.Name, Project.Order, StreamResponses, CategoryResponses, AclResponse); } /// /// Tests if a category response matches a given stream name /// /// The stream name /// The category response /// True if the category matches static bool MatchCategory(string Name, GetProjectCategoryResponse Category) { if (Category.IncludePatterns.Any(x => StreamCategory.MatchPattern(Name, x))) { if (!Category.ExcludePatterns.Any(x => StreamCategory.MatchPattern(Name, x))) { return true; } } return false; } } /// /// Projection of a project definition to just include permissions info /// public interface IProjectPermissions { /// /// ACL for the project /// public Acl? Acl { get; } } }