| File: AppHostServerV2CodeGenerationInput.cs | Web Access |
| Project: src\src\Aspire.Managed\Aspire.Managed.csproj (aspire-managed) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; using System.Text.Json.Serialization; using Aspire.Hosting.Native.Tooling; using Aspire.TypeSystem; namespace Aspire.Managed; internal sealed record AppHostServerV2ProviderInput(string PackageName, IReadOnlyList<NativeProviderExport> Capabilities, IReadOnlyDictionary<string, string> SdkFiles, IReadOnlyList<string> Exports); /// <summary>Loads request files and provider metadata before contract merging or SDK generation.</summary> internal sealed record AppHostServerV2CodeGenerationInput(AtsContext Core, IReadOnlyList<AppHostServerV2ProviderInput> Providers, string Output) { public static async Task<AppHostServerV2CodeGenerationInput> ReadAsync(string requestPath) { // CLI preparation writes {"coreContract":".../ats-contract.json","output":".../sdk", // "providers":[{"packageName":"example.provider","metadata":".../contract.json", // "sdkFiles":{"provider.mts":".../provider.mts"},"exports":["provider.mts"]}]}. var request = JsonSerializer.Deserialize<GenerationRequest>(await File.ReadAllTextAsync(requestPath).ConfigureAwait(false), new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) ?? throw new InvalidDataException("The SDK generation request is missing."); if (string.IsNullOrWhiteSpace(request.CoreContract) || string.IsNullOrWhiteSpace(request.Output) || request.Providers is null) { throw new InvalidDataException("SDK generation requires a core contract, output directory and provider list."); } var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; options.Converters.Add(new JsonStringEnumConverter()); var core = JsonSerializer.Deserialize<AtsContext>( await File.ReadAllTextAsync(request.CoreContract).ConfigureAwait(false), options) ?? throw new InvalidDataException("AppHost Server v2 is missing its core ATS contract."); var providers = new List<AppHostServerV2ProviderInput>(); foreach (var provider in request.Providers) { if (provider is null || string.IsNullOrWhiteSpace(provider.PackageName) || string.IsNullOrWhiteSpace(provider.Metadata) || provider.SdkFiles is null || provider.Exports is null) { throw new InvalidDataException("SDK generation requires complete provider descriptors."); } foreach (var name in provider.SdkFiles.Keys) { if (Path.GetFileName(name) != name || !name.EndsWith(".mts", StringComparison.Ordinal) || name is "native-builder.mts" or "native-client.mts" or "native-types.mts" or "native-pipeline.mts") { throw new InvalidDataException($"Provider SDK file '{name}' is invalid."); } } foreach (var module in provider.Exports) { if (module is null || !provider.SdkFiles.ContainsKey(module)) { throw new InvalidDataException($"Provider entry point '{module}' is not an SDK file."); } } var capabilities = NativeProviderContractReader.Read( await File.ReadAllTextAsync(provider.Metadata).ConfigureAwait(false)); var files = new Dictionary<string, string>(StringComparer.Ordinal); foreach (var (name, path) in provider.SdkFiles) { files.Add(name, await File.ReadAllTextAsync(path).ConfigureAwait(false)); } providers.Add(new(provider.PackageName, capabilities, files, provider.Exports)); } return new(core, providers, request.Output); } private sealed class GenerationRequest { public required string CoreContract { get; init; } public required string Output { get; init; } public required ProviderRequest[] Providers { get; init; } } private sealed class ProviderRequest { public required string PackageName { get; init; } public required string Metadata { get; init; } public required Dictionary<string, string> SdkFiles { get; init; } public required string[] Exports { get; init; } } }