mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-03 19:27:21 +00:00
Generated the JSON contracts of backups and the provenance store at compile time
This commit is contained in:
5 files changed
+58
-4
No files matched your search
@@ -51,12 +51,13 @@ public static class DataBackupFormat
|
|||||||
/// How the manifest and the plan of a restore are written: readable for anybody who opens them,
|
/// How the manifest and the plan of a restore are written: readable for anybody who opens them,
|
||||||
/// and for the runtime, which applies the plan.
|
/// and for the runtime, which applies the plan.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly JsonSerializerOptions JSON_OPTIONS = new()
|
public static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
WriteIndented = true,
|
WriteIndented = true,
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||||
Converters = { new JsonStringEnumConverter() },
|
Converters = { new JsonStringEnumConverter() },
|
||||||
};
|
TypeInfoResolver = DataBackupJsonContext.Default,
|
||||||
|
}.AsReadOnly();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// File types which are compressed already. Compressing them once more costs time and makes the
|
/// File types which are compressed already. Compressing them once more costs time and makes the
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.DataBackup;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The contracts with which System.Text.Json reads and writes the manifest of a backup and the plan and report of
|
||||||
|
/// a restore, built at compile time.
|
||||||
|
/// </summary>
|
||||||
|
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||||
|
[JsonSerializable(typeof(DataBackupManifest))]
|
||||||
|
[JsonSerializable(typeof(DataRestorePlan))]
|
||||||
|
[JsonSerializable(typeof(DataRestoreReport))]
|
||||||
|
internal sealed partial class DataBackupJsonContext : JsonSerializerContext;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AIStudio.Tools.Databases.ProvenanceStore;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The contracts with which System.Text.Json reads and writes the content around a passage, built at compile time.
|
||||||
|
/// </summary>
|
||||||
|
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||||
|
[JsonSerializable(typeof(IReadOnlyList<string>))]
|
||||||
|
[JsonSerializable(typeof(List<string>))]
|
||||||
|
internal sealed partial class ProvenanceJsonContext : JsonSerializerContext;
|
||||||
+6
-2
@@ -39,7 +39,11 @@ public sealed class SqliteProvenanceStoreClientImplementation(string name, strin
|
|||||||
/// Writes the content around a passage the way it reads: the default encoder would turn every
|
/// Writes the content around a passage the way it reads: the default encoder would turn every
|
||||||
/// character outside ASCII into an escape, cf. ToolExecutionResult.MODEL_CONTENT_OPTIONS.
|
/// character outside ASCII into an escape, cf. ToolExecutionResult.MODEL_CONTENT_OPTIONS.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static readonly JsonSerializerOptions JSON_OPTIONS = new() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
internal static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||||
|
TypeInfoResolver = ProvenanceJsonContext.Default,
|
||||||
|
}.AsReadOnly();
|
||||||
|
|
||||||
private readonly string databasePath = databasePath;
|
private readonly string databasePath = databasePath;
|
||||||
private readonly string lastCleanupPath = Path.Join(basePath, LAST_CLEANUP_FILENAME);
|
private readonly string lastCleanupPath = Path.Join(basePath, LAST_CLEANUP_FILENAME);
|
||||||
@@ -351,7 +355,7 @@ public sealed class SqliteProvenanceStoreClientImplementation(string name, strin
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return JsonSerializer.Deserialize<List<string>>(row.SurroundingContent) ?? [];
|
return JsonSerializer.Deserialize<List<string>>(row.SurroundingContent, JSON_OPTIONS) ?? [];
|
||||||
}
|
}
|
||||||
catch (JsonException exception)
|
catch (JsonException exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AIStudio.Tests.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks that every contract AI Studio uses for its JSON comes from source generation, never from reflection.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A type which no context of the options knows makes System.Text.Json throw, unless the options fall back to
|
||||||
|
/// reflection. Such a fallback hides the missing type until a trimmed build removes what reflection needs, and
|
||||||
|
/// only a published build shows it then.
|
||||||
|
/// </remarks>
|
||||||
|
[TestFixture]
|
||||||
|
public sealed class JsonCoverageTests
|
||||||
|
{
|
||||||
|
[TestCaseSource(typeof(JsonTestRoots), nameof(JsonTestRoots.Stored))]
|
||||||
|
public void EveryContractComesFromSourceGeneration(JsonTestRoot root)
|
||||||
|
{
|
||||||
|
var typesWithoutGeneratedContract = JsonTypeGraph.Collect(root.Type, root.Options)
|
||||||
|
.Where(type => root.Options.GetTypeInfo(type).OriginatingResolver is not JsonSerializerContext)
|
||||||
|
.Select(JsonContractDescription.TypeName);
|
||||||
|
|
||||||
|
Assert.That(typesWithoutGeneratedContract, Is.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user