tools/code/common/WorkspaceBackend.cs (274 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.Nodes; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; namespace common; public sealed record WorkspaceBackendsUri : ResourceUri { public required WorkspaceUri Parent { get; init; } private static string PathSegment { get; } = "backends"; protected override Uri Value => Parent.ToUri().AppendPathSegment(PathSegment).ToUri(); public static WorkspaceBackendsUri From(WorkspaceName workspaceName, ManagementServiceUri serviceUri) => new() { Parent = WorkspaceUri.From(workspaceName, serviceUri) }; } public sealed record WorkspaceBackendUri : ResourceUri { public required WorkspaceBackendsUri Parent { get; init; } public required BackendName Name { get; init; } protected override Uri Value => Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri(); public static WorkspaceBackendUri From(BackendName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) => new() { Parent = WorkspaceBackendsUri.From(workspaceName, serviceUri), Name = name }; } public sealed record WorkspaceBackendsDirectory : ResourceDirectory { public required WorkspaceDirectory Parent { get; init; } private static string Name { get; } = "backends"; protected override DirectoryInfo Value => Parent.ToDirectoryInfo().GetChildDirectory(Name); public static WorkspaceBackendsDirectory From(WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) => new() { Parent = WorkspaceDirectory.From(workspaceName, serviceDirectory) }; public static Option<WorkspaceBackendsDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) => directory?.Name == Name ? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory) select new WorkspaceBackendsDirectory { Parent = parent } : Option<WorkspaceBackendsDirectory>.None; } public sealed record WorkspaceBackendDirectory : ResourceDirectory { public required WorkspaceBackendsDirectory Parent { get; init; } public required BackendName Name { get; init; } protected override DirectoryInfo Value => Parent.ToDirectoryInfo().GetChildDirectory(Name.Value); public static WorkspaceBackendDirectory From(BackendName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) => new() { Parent = WorkspaceBackendsDirectory.From(workspaceName, serviceDirectory), Name = name }; public static Option<WorkspaceBackendDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) => from parent in WorkspaceBackendsDirectory.TryParse(directory?.Parent, serviceDirectory) let name = BackendName.From(directory!.Name) select new WorkspaceBackendDirectory { Parent = parent, Name = name }; } public sealed record WorkspaceBackendInformationFile : ResourceFile { public required WorkspaceBackendDirectory Parent { get; init; } private static string Name { get; } = "backendInformation.json"; protected override FileInfo Value => Parent.ToDirectoryInfo().GetChildFile(Name); public static WorkspaceBackendInformationFile From(BackendName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) => new() { Parent = WorkspaceBackendDirectory.From(name, workspaceName, serviceDirectory) }; public static Option<WorkspaceBackendInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) => file?.Name == Name ? from parent in WorkspaceBackendDirectory.TryParse(file.Directory, serviceDirectory) select new WorkspaceBackendInformationFile { Parent = parent } : Option<WorkspaceBackendInformationFile>.None; } public sealed record WorkspaceBackendDto { [JsonPropertyName("properties")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public required BackendContract Properties { get; init; } public record BackendContract { [JsonPropertyName("credentials")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public BackendCredentialsContract? Credentials { get; init; } [JsonPropertyName("description")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Description { get; init; } [JsonPropertyName("properties")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public BackendProperties? Properties { get; init; } [JsonPropertyName("protocol")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Protocol { get; init; } [JsonPropertyName("proxy")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public BackendProxyContract? Proxy { get; init; } [JsonPropertyName("resourceId")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? ResourceId { get; init; } [JsonPropertyName("title")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Title { get; init; } [JsonPropertyName("tls")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public BackendTlsProperties? Tls { get; init; } [JsonPropertyName("url")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] #pragma warning disable CA1056 // URI-like properties should not be strings public string? Url { get; init; } #pragma warning restore CA1056 // URI-like properties should not be strings } public record BackendCredentialsContract { [JsonPropertyName("authorization")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public BackendAuthorizationHeaderCredentials? Authorization { get; init; } [JsonPropertyName("certificate")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableList<string>? Certificate { get; init; } [JsonPropertyName("certificateIds")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableList<string>? CertificateIds { get; init; } [JsonPropertyName("header")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonObject? Header { get; init; } [JsonPropertyName("query")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public JsonObject? Query { get; init; } } public record BackendAuthorizationHeaderCredentials { [JsonPropertyName("parameter")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Parameter { get; init; } [JsonPropertyName("scheme")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Scheme { get; init; } } public record BackendProperties { [JsonPropertyName("serviceFabricCluster")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public BackendServiceFabricClusterProperties? ServiceFabricCluster { get; init; } } public record BackendProxyContract { [JsonPropertyName("password")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Password { get; init; } [JsonPropertyName("url")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] #pragma warning disable CA1056 // URI-like properties should not be strings public string? Url { get; init; } #pragma warning restore CA1056 // URI-like properties should not be strings [JsonPropertyName("username")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Username { get; init; } } public record BackendServiceFabricClusterProperties { [JsonPropertyName("clientCertificateId")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? ClientCertificateId { get; init; } [JsonPropertyName("clientCertificatethumbprint")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? ClientCertificateThumbprint { get; init; } [JsonPropertyName("managementEndpoints")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableList<string>? ManagementEndpoints { get; init; } [JsonPropertyName("maxPartitionResolutionRetries")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int? MaxPartitionResolutionRetries { get; init; } [JsonPropertyName("serverCertificateThumbprints")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableList<string>? ServerCertificateThumbprints { get; init; } [JsonPropertyName("serverX509Names")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public ImmutableList<X509CertificateName>? ServerX509Names { get; init; } } public record BackendTlsProperties { [JsonPropertyName("validateCertificateChain")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool? ValidateCertificateChain { get; init; } [JsonPropertyName("validateCertificateName")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool? ValidateCertificateName { get; init; } } public record X509CertificateName { [JsonPropertyName("issuerCertificateThumbprint")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? IssuerCertificateThumbprint { get; init; } [JsonPropertyName("name")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string? Name { get; init; } } } public static class WorkspaceBackendModule { public static async ValueTask DeleteAll(this WorkspaceBackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => await uri.ListNames(pipeline, cancellationToken) .IterParallel(async name => { var resourceUri = new WorkspaceBackendUri { Parent = uri, Name = name }; await resourceUri.Delete(pipeline, cancellationToken); }, cancellationToken); public static IAsyncEnumerable<BackendName> ListNames(this WorkspaceBackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => pipeline.ListJsonObjects(uri.ToUri(), cancellationToken) .Select(jsonObject => jsonObject.GetStringProperty("name")) .Select(BackendName.From); public static IAsyncEnumerable<(BackendName Name, WorkspaceBackendDto Dto)> List(this WorkspaceBackendsUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => uri.ListNames(pipeline, cancellationToken) .SelectAwait(async name => { var resourceUri = new WorkspaceBackendUri { Parent = uri, Name = name }; var dto = await resourceUri.GetDto(pipeline, cancellationToken); return (name, dto); }); public static async ValueTask<WorkspaceBackendDto> GetDto(this WorkspaceBackendUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) { var content = await pipeline.GetContent(uri.ToUri(), cancellationToken); return content.ToObjectFromJson<WorkspaceBackendDto>(); } public static async ValueTask Delete(this WorkspaceBackendUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) => await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken); public static async ValueTask PutDto(this WorkspaceBackendUri uri, WorkspaceBackendDto dto, HttpPipeline pipeline, CancellationToken cancellationToken) { var content = BinaryData.FromObjectAsJson(dto); await pipeline.PutContent(uri.ToUri(), content, cancellationToken); } public static IEnumerable<WorkspaceBackendDirectory> ListDirectories(ManagementServiceDirectory serviceDirectory) => from workspaceDirectory in WorkspaceModule.ListDirectories(serviceDirectory) let workspacebackendsDirectory = new WorkspaceBackendsDirectory { Parent = workspaceDirectory } where workspacebackendsDirectory.ToDirectoryInfo().Exists() from workspaceBackendDirectoryInfo in workspacebackendsDirectory.ToDirectoryInfo().ListDirectories("*") let name = BackendName.From(workspaceBackendDirectoryInfo.Name) select new WorkspaceBackendDirectory { Parent = workspacebackendsDirectory, Name = name }; public static IEnumerable<WorkspaceBackendInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) => from workspaceBackendDirectory in ListDirectories(serviceDirectory) let informationFile = new WorkspaceBackendInformationFile { Parent = workspaceBackendDirectory } where informationFile.ToFileInfo().Exists() select informationFile; public static async ValueTask WriteDto(this WorkspaceBackendInformationFile file, WorkspaceBackendDto dto, CancellationToken cancellationToken) { var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions); await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken); } public static async ValueTask<WorkspaceBackendDto> ReadDto(this WorkspaceBackendInformationFile file, CancellationToken cancellationToken) { var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken); return content.ToObjectFromJson<WorkspaceBackendDto>(); } }