work around sequential schedule validation limit (#2655)

* remove readalltext

* remove unused method

* remove fileexists

* remove folderexists

* remove readalllines

* remove fake local file system

* show playlist name in playout build errors

* add basic sequential schedule validator tests

* work around sequential schedule validation limit
This commit is contained in:
Jason Dove
2025-11-24 12:08:43 -06:00
committed by GitHub
parent e40d192aea
commit ec0d8ea6ac
82 changed files with 841 additions and 750 deletions
@@ -1,5 +1,7 @@
using System.Globalization;
using System.IO.Abstractions;
using System.Text.Json;
using System.Text.Json.Nodes;
using ErsatzTV.Core;
using ErsatzTV.Core.Interfaces.Scheduling;
using Microsoft.Extensions.Logging;
@@ -8,30 +10,30 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;
using YamlDotNet.RepresentationModel;
using JsonSerializer = System.Text.Json.JsonSerializer;
using JsonSchemaNet = Json.Schema;
namespace ErsatzTV.Infrastructure.Scheduling;
public class SequentialScheduleValidator(ILogger<SequentialScheduleValidator> logger) : ISequentialScheduleValidator
public class SequentialScheduleValidator(IFileSystem fileSystem, ILogger<SequentialScheduleValidator> logger)
: ISequentialScheduleValidator
{
public async Task<bool> ValidateSchedule(string yaml, bool isImport)
{
try
{
string schemaFileName = Path.Combine(
FileSystemLayout.ResourcesCacheFolder,
isImport ? "sequential-schedule-import.schema.json" : "sequential-schedule.schema.json");
using StreamReader sr = File.OpenText(schemaFileName);
await using var reader = new JsonTextReader(sr);
var schema = JSchema.Load(reader);
string schemaFileName = GetSchemaPath(isImport);
string schemaText = await fileSystem.File.ReadAllTextAsync(schemaFileName);
using var textReader = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(textReader);
var schedule = JObject.Parse(Convert(yamlStream));
JsonSchemaNet.JsonSchema schema = JsonSchemaNet.JsonSchema.FromText(schemaText);
if (!schedule.IsValid(schema, out IList<string> errorMessages))
string jsonString = ConvertYamlToJsonString(yaml);
JsonNode jsonNode = JsonNode.Parse(jsonString);
JsonSchemaNet.EvaluationResults result = schema.Evaluate(jsonNode);
if (!result.IsValid)
{
logger.LogWarning("Failed to validate sequential schedule definition: {ErrorMessages}", errorMessages);
logger.LogWarning("Sequential schedule definition failed validation");
return false;
}
@@ -47,30 +49,27 @@ public class SequentialScheduleValidator(ILogger<SequentialScheduleValidator> lo
public string ToJson(string yaml)
{
using var textReader = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(textReader);
var schedule = JObject.Parse(Convert(yamlStream));
string jsonString = ConvertYamlToJsonString(yaml);
var schedule = JObject.Parse(jsonString);
string formatted = JsonConvert.SerializeObject(schedule, Formatting.Indented);
string[] lines = formatted.Split('\n');
return string.Join('\n', lines.Select((line, index) => $"{index + 1,4}: {line}"));
}
// limited to 1000/hr, but only called manually from UI
public async Task<IList<string>> GetValidationMessages(string yaml, bool isImport)
{
try
{
string schemaFileName = Path.Combine(
FileSystemLayout.ResourcesCacheFolder,
isImport ? "sequential-schedule-import.schema.json" : "sequential-schedule.schema.json");
using StreamReader sr = File.OpenText(schemaFileName);
string schemaFileName = GetSchemaPath(isImport);
using StreamReader sr = fileSystem.File.OpenText(schemaFileName);
await using var reader = new JsonTextReader(sr);
var schema = JSchema.Load(reader);
using var textReader = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(textReader);
var schedule = JObject.Parse(Convert(yamlStream));
string jsonString = ConvertYamlToJsonString(yaml);
var schedule = JObject.Parse(jsonString);
return schedule.IsValid(schema, out IList<string> errorMessages) ? [] : errorMessages;
}
@@ -80,13 +79,24 @@ public class SequentialScheduleValidator(ILogger<SequentialScheduleValidator> lo
}
}
private static string Convert(YamlStream yamlStream)
private static string ConvertYamlToJsonString(string yaml)
{
using var textReader = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(textReader);
var visitor = new YamlToJsonVisitor();
yamlStream.Accept(visitor);
return JsonConvert.SerializeObject(JsonConvert.DeserializeObject(visitor.JsonString), Formatting.Indented);
}
private static string GetSchemaPath(bool isImport)
{
return Path.Combine(
FileSystemLayout.ResourcesCacheFolder,
isImport ? "sequential-schedule-import.schema.json" : "sequential-schedule.schema.json");
}
private sealed class YamlToJsonVisitor : IYamlVisitor
{
private readonly JsonSerializerOptions _options = new() { WriteIndented = false };