tools/code/common/ApiDiagnostic.cs (247 lines of code) (raw):

using Azure.Core.Pipeline; using Flurl; using LanguageExt; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; namespace common; public sealed record ApiDiagnosticName : ResourceName, IResourceName<ApiDiagnosticName> { private ApiDiagnosticName(string value) : base(value) { } public static ApiDiagnosticName From(string value) => new(value); } public sealed record ApiDiagnosticsUri : ResourceUri { public required ApiUri Parent { get; init; } private static string PathSegment { get; } = "diagnostics"; protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri(); public static ApiDiagnosticsUri From(ApiName apiName, ManagementServiceUri serviceUri) => new() { Parent = ApiUri.From(apiName, serviceUri) }; } public sealed record ApiDiagnosticUri : ResourceUri { public required ApiDiagnosticsUri Parent { get; init; } public required ApiDiagnosticName Name { get; init; } protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri(); public static ApiDiagnosticUri From(ApiDiagnosticName name, ApiName apiName, ManagementServiceUri serviceUri) => new() { Parent = ApiDiagnosticsUri.From(apiName, serviceUri), Name = name }; } public sealed record ApiDiagnosticsDirectory : ResourceDirectory { public required ApiDirectory Parent { get; init; } private static string Name { get; } = "diagnostics"; protected override DirectoryInfo Value => Parent.ToDirectoryInfo().GetChildDirectory(Name); public static ApiDiagnosticsDirectory From(ApiName apiName, ManagementServiceDirectory serviceDirectory) => new() { Parent = ApiDirectory.From(apiName, serviceDirectory) }; public static Option<ApiDiagnosticsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) => directory?.Name == Name ? from parent in ApiDirectory.TryParse(directory.Parent, serviceDirectory) select new ApiDiagnosticsDirectory { Parent = parent } : Option<ApiDiagnosticsDirectory>.None; } public sealed record ApiDiagnosticDirectory : ResourceDirectory { public required ApiDiagnosticsDirectory Parent { get; init; } public required ApiDiagnosticName Name { get; init; } protected override DirectoryInfo Value => Parent.ToDirectoryInfo().GetChildDirectory(Name.Value); public static ApiDiagnosticDirectory From(ApiDiagnosticName name, ApiName apiName, ManagementServiceDirectory serviceDirectory) => new() { Parent = ApiDiagnosticsDirectory.From(apiName, serviceDirectory), Name = name }; public static Option<ApiDiagnosticDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) => from parent in ApiDiagnosticsDirectory.TryParse(directory?.Parent, serviceDirectory) let name = ApiDiagnosticName.From(directory!.Name) select new ApiDiagnosticDirectory { Parent = parent, Name = name }; } public sealed record ApiDiagnosticInformationFile : ResourceFile { public required ApiDiagnosticDirectory Parent { get; init; } private static string Name { get; } = "diagnosticInformation.json"; protected override FileInfo Value => Parent.ToDirectoryInfo().GetChildFile(Name); public static ApiDiagnosticInformationFile From(ApiDiagnosticName name, ApiName apiName, ManagementServiceDirectory serviceDirectory) => new() { Parent = ApiDiagnosticDirectory.From(name, apiName, serviceDirectory) }; public static Option<ApiDiagnosticInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) => file?.Name == Name ? from parent in ApiDiagnosticDirectory.TryParse(file.Directory, serviceDirectory) select new ApiDiagnosticInformationFile { Parent = parent } : Option<ApiDiagnosticInformationFile>.None; } public sealed record ApiDiagnosticDto { [JsonPropertyName("properties")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public required DiagnosticContract Properties { get; init; } public sealed record DiagnosticContract { [JsonPropertyName("loggerId")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? LoggerId { get; init; } [JsonPropertyName("alwaysLog")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? AlwaysLog { get; init; } [JsonPropertyName("backend")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public PipelineDiagnosticSettings? Backend { get; init; } [JsonPropertyName("frontend")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public PipelineDiagnosticSettings? Frontend { get; init; } [JsonPropertyName("httpCorrelationProtocol")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? HttpCorrelationProtocol { get; init; } [JsonPropertyName("logClientIp")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool? LogClientIp { get; init; } [JsonPropertyName("metrics")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool? Metrics { get; init; } [JsonPropertyName("operationNameFormat")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? OperationNameFormat { get; init; } [JsonPropertyName("sampling")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public SamplingSettings? Sampling { get; init; } [JsonPropertyName("verbosity")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Verbosity { get; init; } } public sealed record PipelineDiagnosticSettings { [JsonPropertyName("request")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public HttpMessageDiagnostic? Request { get; init; } [JsonPropertyName("response")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public HttpMessageDiagnostic? Response { get; init; } } public sealed record HttpMessageDiagnostic { [JsonPropertyName("body")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public BodyDiagnosticSettings? Body { get; init; } [JsonPropertyName("dataMasking")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public DataMasking? DataMasking { get; init; } [JsonPropertyName("headers")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableArray<string>? Headers { get; init; } } public sealed record BodyDiagnosticSettings { [JsonPropertyName("bytes")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int? Bytes { get; init; } } public sealed record DataMasking { [JsonPropertyName("headers")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableArray<DataMaskingEntity>? Headers { get; init; } [JsonPropertyName("queryParams")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableArray<DataMaskingEntity>? QueryParams { get; init; } } public sealed record DataMaskingEntity { [JsonPropertyName("mode")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Mode { get; init; } [JsonPropertyName("value")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Value { get; init; } } public sealed record SamplingSettings { [JsonPropertyName("percentage")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public float? Percentage { get; init; } [JsonPropertyName("samplingType")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? SamplingType { get; init; } } } public static class ApiDiagnosticModule { public static async ValueTask DeleteAll(this ApiDiagnosticsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => await uri.ListNames(pipeline, cancellationToken) .IterParallel(async name => { var resourceUri = new ApiDiagnosticUri { Parent = uri, Name = name }; await resourceUri.Delete(pipeline, cancellationToken); }, cancellationToken); public static IAsyncEnumerable<ApiDiagnosticName> ListNames(this ApiDiagnosticsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => pipeline.ListJsonObjects(uri.ToUri(), cancellationToken) .Select(jsonObject => jsonObject.GetStringProperty("name")) .Select(ApiDiagnosticName.From); public static IAsyncEnumerable<(ApiDiagnosticName Name, ApiDiagnosticDto Dto)> List(this ApiDiagnosticsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => uri.ListNames(pipeline, cancellationToken) .SelectAwait(async name => { var resourceUri = new ApiDiagnosticUri { Parent = uri, Name = name }; var dto = await resourceUri.GetDto(pipeline, cancellationToken); return (name, dto); }); public static async ValueTask<ApiDiagnosticDto> GetDto(this ApiDiagnosticUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) { var content = await pipeline.GetContent(uri.ToUri(), cancellationToken); return content.ToObjectFromJson<ApiDiagnosticDto>(); } public static async ValueTask Delete(this ApiDiagnosticUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken); public static async ValueTask PutDto(this ApiDiagnosticUri uri, ApiDiagnosticDto dto, HttpPipeline pipeline, CancellationToken cancellationToken) { var content = BinaryData.FromObjectAsJson(dto); await pipeline.PutContent(uri.ToUri(), content, cancellationToken); } public static IEnumerable<ApiDiagnosticDirectory> ListDirectories(ManagementServiceDirectory serviceDirectory) => from apiDirectory in ApiModule.ListDirectories(serviceDirectory) let diagnosticsDirectory = new ApiDiagnosticsDirectory { Parent = apiDirectory } where diagnosticsDirectory.ToDirectoryInfo().Exists() from diagnosticDirectoryInfo in diagnosticsDirectory.ToDirectoryInfo().ListDirectories("*") let name = ApiDiagnosticName.From(diagnosticDirectoryInfo.Name) select new ApiDiagnosticDirectory { Parent = diagnosticsDirectory, Name = name }; public static IEnumerable<ApiDiagnosticInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) => from diagnosticDirectory in ListDirectories(serviceDirectory) let informationFile = new ApiDiagnosticInformationFile { Parent = diagnosticDirectory } where informationFile.ToFileInfo().Exists() select informationFile; public static async ValueTask WriteDto(this ApiDiagnosticInformationFile file, ApiDiagnosticDto dto, CancellationToken cancellationToken) { var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions); await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken); } public static async ValueTask<ApiDiagnosticDto> ReadDto(this ApiDiagnosticInformationFile file, CancellationToken cancellationToken) { var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken); return content.ToObjectFromJson<ApiDiagnosticDto>(); } }