tools/code/common/WorkspaceApi.cs (553 lines of code) (raw):
using Azure;
using Azure.Core;
using Azure.Core.Pipeline;
using Flurl;
using LanguageExt;
using Polly;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using YamlDotNet.System.Text.Json;
namespace common;
public sealed record WorkspaceApisUri : ResourceUri
{
public required WorkspaceUri Parent { get; init; }
private static string PathSegment { get; } = "apis";
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(PathSegment).ToUri();
public static WorkspaceApisUri From(WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new() { Parent = WorkspaceUri.From(workspaceName, serviceUri) };
}
public sealed record WorkspaceApiUri : ResourceUri
{
public required WorkspaceApisUri Parent { get; init; }
public required ApiName Name { get; init; }
protected override Uri Value =>
Parent.ToUri().AppendPathSegment(Name.ToString()).ToUri();
public static WorkspaceApiUri From(ApiName name, WorkspaceName workspaceName, ManagementServiceUri serviceUri) =>
new()
{
Parent = WorkspaceApisUri.From(workspaceName, serviceUri),
Name = name
};
}
public sealed record WorkspaceApisDirectory : ResourceDirectory
{
public required WorkspaceDirectory Parent { get; init; }
private static string Name { get; } = "apis";
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name);
public static WorkspaceApisDirectory From(WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new() { Parent = WorkspaceDirectory.From(workspaceName, serviceDirectory) };
public static Option<WorkspaceApisDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null
&& directory.Name == Name
? from parent in WorkspaceDirectory.TryParse(directory.Parent, serviceDirectory)
select new WorkspaceApisDirectory { Parent = parent }
: Option<WorkspaceApisDirectory>.None;
}
public sealed record WorkspaceApiDirectory : ResourceDirectory
{
public required WorkspaceApisDirectory Parent { get; init; }
public required ApiName Name { get; init; }
protected override DirectoryInfo Value =>
Parent.ToDirectoryInfo().GetChildDirectory(Name.Value);
public static WorkspaceApiDirectory From(ApiName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApisDirectory.From(workspaceName, serviceDirectory),
Name = name
};
public static Option<WorkspaceApiDirectory> TryParse(DirectoryInfo? directory, ManagementServiceDirectory serviceDirectory) =>
directory is not null
? from parent in WorkspaceApisDirectory.TryParse(directory?.Parent, serviceDirectory)
let name = ApiName.From(directory!.Name)
select new WorkspaceApiDirectory
{
Parent = parent,
Name = name
}
: Option<WorkspaceApiDirectory>.None;
}
public sealed record WorkspaceApiInformationFile : ResourceFile
{
public required WorkspaceApiDirectory Parent { get; init; }
public static string Name { get; } = "apiInformation.json";
protected override FileInfo Value =>
Parent.ToDirectoryInfo().GetChildFile(Name);
public static WorkspaceApiInformationFile From(ApiName name, WorkspaceName workspaceName, ManagementServiceDirectory serviceDirectory) =>
new()
{
Parent = WorkspaceApiDirectory.From(name, workspaceName, serviceDirectory)
};
public static Option<WorkspaceApiInformationFile> TryParse(FileInfo? file, ManagementServiceDirectory serviceDirectory) =>
file is not null &&
file.Name == Name
? from parent in WorkspaceApiDirectory.TryParse(file.Directory, serviceDirectory)
select new WorkspaceApiInformationFile
{
Parent = parent
}
: Option<WorkspaceApiInformationFile>.None;
}
public sealed record WorkspaceApiDto
{
[JsonPropertyName("properties")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public required ApiCreateOrUpdateProperties Properties { get; init; }
public record ApiCreateOrUpdateProperties
{
[JsonPropertyName("path")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Path { get; init; }
[JsonPropertyName("apiRevision")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiRevision { get; init; }
[JsonPropertyName("apiRevisionDescription")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiRevisionDescription { get; init; }
[JsonPropertyName("apiVersion")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiVersion { get; init; }
[JsonPropertyName("apiVersionDescription")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiVersionDescription { get; init; }
[JsonPropertyName("apiVersionSetId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiVersionSetId { get; init; }
[JsonPropertyName("authenticationSettings")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public AuthenticationSettingsContract? AuthenticationSettings { get; init; }
[JsonPropertyName("contact")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ApiContactInformation? Contact { get; init; }
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("isCurrent")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? IsCurrent { get; init; }
[JsonPropertyName("license")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ApiLicenseInformation? License { get; init; }
[JsonPropertyName("apiType")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? ApiType { get; init; }
[JsonPropertyName("apiVersionSet")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ApiVersionSetContractDetails? ApiVersionSet { get; init; }
[JsonPropertyName("displayName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? DisplayName { get; init; }
[JsonPropertyName("format")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Format { get; init; }
[JsonPropertyName("protocols")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<string>? Protocols { get; init; }
[JsonPropertyName("serviceUrl")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? ServiceUrl { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
[JsonPropertyName("sourceApiId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? SourceApiId { get; init; }
[JsonPropertyName("translateRequiredQueryParameters")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? TranslateRequiredQueryParameters { get; init; }
[JsonPropertyName("value")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Value { get; init; }
[JsonPropertyName("wsdlSelector")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public WsdlSelectorContract? WsdlSelector { get; init; }
[JsonPropertyName("subscriptionKeyParameterNames")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public SubscriptionKeyParameterNamesContract? SubscriptionKeyParameterNames { get; init; }
[JsonPropertyName("subscriptionRequired")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public bool? SubscriptionRequired { get; init; }
[JsonPropertyName("termsOfServiceUrl")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
#pragma warning disable CA1056 // URI-like properties should not be strings
public string? TermsOfServiceUrl { get; init; }
#pragma warning restore CA1056 // URI-like properties should not be strings
[JsonPropertyName("type")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Type { get; init; }
public record AuthenticationSettingsContract
{
[JsonPropertyName("oAuth2")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public OAuth2AuthenticationSettingsContract? OAuth2 { get; init; }
[JsonPropertyName("openid")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public OpenIdAuthenticationSettingsContract? OpenId { get; init; }
}
public record OAuth2AuthenticationSettingsContract
{
[JsonPropertyName("authorizationServerId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? AuthorizationServerId { get; init; }
[JsonPropertyName("scope")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Scope { get; init; }
}
public record OpenIdAuthenticationSettingsContract
{
[JsonPropertyName("bearerTokenSendingMethods")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public ImmutableArray<string>? BearerTokenSendingMethods { get; init; }
[JsonPropertyName("openidProviderId")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? OpenIdProviderId { get; init; }
}
public record ApiContactInformation
{
[JsonPropertyName("email")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Email { get; init; }
[JsonPropertyName("name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Name { 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 ApiLicenseInformation
{
[JsonPropertyName("name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Name { 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 ApiVersionSetContractDetails
{
[JsonPropertyName("description")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Description { get; init; }
[JsonPropertyName("id")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Id { get; init; }
[JsonPropertyName("name")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Name { get; init; }
[JsonPropertyName("versionHeaderName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersionHeaderName { get; init; }
[JsonPropertyName("versionQueryName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersionQueryName { get; init; }
[JsonPropertyName("versioningScheme")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? VersioningScheme { get; init; }
}
public record SubscriptionKeyParameterNamesContract
{
[JsonPropertyName("header")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Header { get; init; }
[JsonPropertyName("query")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? Query { get; init; }
}
public record WsdlSelectorContract
{
[JsonPropertyName("wsdlEndpointName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? WsdlEndpointName { get; init; }
[JsonPropertyName("wsdlServiceName")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public string? WsdlServiceName { get; init; }
}
}
}
public static class WorkspaceApiModule
{
public static async ValueTask DeleteAll(this WorkspaceApisUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await uri.ListNames(pipeline, cancellationToken)
.IterParallel(async name =>
{
var resourceUri = new WorkspaceApiUri { Parent = uri, Name = name };
await resourceUri.Delete(pipeline, cancellationToken);
}, cancellationToken);
public static IAsyncEnumerable<ApiName> ListNames(this WorkspaceApisUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
pipeline.ListJsonObjects(uri.ToUri(), cancellationToken)
.Select(jsonObject => jsonObject.GetStringProperty("name"))
.Select(ApiName.From);
public static IAsyncEnumerable<(ApiName Name, WorkspaceApiDto Dto)> List(this WorkspaceApisUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
uri.ListNames(pipeline, cancellationToken)
.SelectAwait(async name =>
{
var resourceUri = new WorkspaceApiUri { Parent = uri, Name = name };
var dto = await resourceUri.GetDto(pipeline, cancellationToken);
return (name, dto);
});
public static async ValueTask<WorkspaceApiDto> GetDto(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var content = await pipeline.GetContent(uri.ToUri(), cancellationToken);
return content.ToObjectFromJson<WorkspaceApiDto>();
}
public static async ValueTask<Option<BinaryData>> TryGetSpecificationContents(this WorkspaceApiUri apiUri, ApiSpecification specification, HttpPipeline pipeline, CancellationToken cancellationToken)
{
if (specification is ApiSpecification.GraphQl)
{
return await apiUri.TryGetGraphQlSchema(pipeline, cancellationToken);
}
BinaryData? content;
try
{
var exportUri = GetExportUri(apiUri, specification, includeLink: true);
var downloadUri = await GetSpecificationDownloadUri(exportUri, pipeline, cancellationToken);
var nonAuthenticatedHttpPipeline = HttpPipelineBuilder.Build(ClientOptions.Default);
content = await nonAuthenticatedHttpPipeline.GetContent(downloadUri, cancellationToken);
}
// If we can't download the specification through the download link, get it directly.
catch (HttpRequestException exception) when (exception.StatusCode == HttpStatusCode.InternalServerError)
{
// Don't export XML specifications, as the non-link exports cannot be reimported.
if (specification is ApiSpecification.Wsdl or ApiSpecification.Wadl)
{
return Option<BinaryData>.None;
}
var exportUri = GetExportUri(apiUri, specification, includeLink: false);
var json = await pipeline.GetJsonObject(exportUri, cancellationToken);
var contentString = json.GetProperty("value") switch
{
JsonValue jsonValue => jsonValue.ToString(),
var node => node.ToJsonString(JsonObjectExtensions.SerializerOptions)
};
content = BinaryData.FromString(contentString);
}
// APIM exports OpenApiV2 to JSON. Convert to YAML if needed.
if (specification is ApiSpecification.OpenApi openApi && openApi.Format is OpenApiFormat.Yaml && openApi.Version is OpenApiVersion.V2)
{
var yaml = YamlConverter.SerializeJson(content.ToString());
content = BinaryData.FromString(yaml);
}
return content;
}
private static Uri GetExportUri(WorkspaceApiUri apiUri, ApiSpecification specification, bool includeLink)
{
var format = GetExportFormat(specification, includeLink);
return apiUri.ToUri()
.SetQueryParam("format", format)
.SetQueryParam("export", "true")
.SetQueryParam("api-version", "2022-09-01-preview")
.ToUri();
}
private static string GetExportFormat(ApiSpecification specification, bool includeLink)
{
var formatWithoutLink = specification switch
{
ApiSpecification.Wadl => "wadl",
ApiSpecification.Wsdl => "wsdl",
ApiSpecification.OpenApi openApiSpecification =>
(openApiSpecification.Version, openApiSpecification.Format) switch
{
(OpenApiVersion.V2, _) => "swagger",
(OpenApiVersion.V3, OpenApiFormat.Yaml) => "openapi",
(OpenApiVersion.V3, OpenApiFormat.Json) => "openapi+json",
_ => throw new NotSupportedException()
},
_ => throw new NotSupportedException()
};
return includeLink ? $"{formatWithoutLink}-link" : formatWithoutLink;
}
private static async ValueTask<Uri> GetSpecificationDownloadUri(Uri exportUri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var json = await pipeline.GetJsonObject(exportUri, cancellationToken);
return json.GetJsonObjectProperty("properties")
.GetJsonObjectProperty("value")
.GetAbsoluteUriProperty("link");
}
public static async ValueTask Delete(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask DeleteAllRevisions(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken) =>
await pipeline.DeleteResource(uri.ToUri()
.SetQueryParam("deleteRevisions", "true")
.ToUri(), waitForCompletion: true, cancellationToken);
public static async ValueTask PutDto(this WorkspaceApiUri uri, WorkspaceApiDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
if (dto.Properties.Format is null && dto.Properties.Value is null)
{
var content = BinaryData.FromObjectAsJson(dto);
await pipeline.PutContent(uri.ToUri(), content, cancellationToken);
}
else
{
if (dto.Properties.Type is "soap")
{
await PutSoapApi(uri, dto, pipeline, cancellationToken);
}
else
{
await PutNonSoapApi(uri, dto, pipeline, cancellationToken);
}
}
using var _ =
await new ResiliencePipelineBuilder<Response>()
.AddRetry(new()
{
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
MaxRetryAttempts = 5,
ShouldHandle = new PredicateBuilder<Response>().HandleResult(CreationInProgress)
})
.Build()
.ExecuteAsync(async cancellationToken =>
{
using var request = pipeline.CreateRequest(uri.ToUri(), RequestMethod.Get);
return await pipeline.SendRequestAsync(request, cancellationToken);
}, cancellationToken);
}
private static async ValueTask PutSoapApi(WorkspaceApiUri uri, WorkspaceApiDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
// Import API with specification
var soapUri = uri.ToUri().SetQueryParam("import", "true").ToUri();
var soapDto = new ApiDto
{
Properties = new ApiDto.ApiCreateOrUpdateProperties
{
Format = "wsdl",
Value = dto.Properties.Value,
ApiType = "soap",
DisplayName = dto.Properties.DisplayName,
Path = dto.Properties.Path,
Protocols = dto.Properties.Protocols,
ApiVersion = dto.Properties.ApiVersion,
ApiVersionDescription = dto.Properties.ApiVersionDescription,
ApiVersionSetId = dto.Properties.ApiVersionSetId
}
};
await pipeline.PutContent(soapUri, BinaryData.FromObjectAsJson(soapDto), cancellationToken);
// Put API again without specification
var updatedDto = dto with { Properties = dto.Properties with { Format = null, Value = null } };
// SOAP apis sometimes fail on put; retry if needed
await soapApiResiliencePipeline.Value
.ExecuteAsync(async cancellationToken => await pipeline.PutContent(uri.ToUri(), BinaryData.FromObjectAsJson(updatedDto), cancellationToken), cancellationToken);
}
private static readonly Lazy<ResiliencePipeline> soapApiResiliencePipeline = new(() =>
new ResiliencePipelineBuilder()
.AddRetry(new()
{
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
MaxRetryAttempts = 3,
ShouldHandle = new PredicateBuilder().Handle<HttpRequestException>(exception => exception.StatusCode == HttpStatusCode.Conflict && exception.Message.Contains("IdentifierAlreadyInUse", StringComparison.OrdinalIgnoreCase))
})
.Build());
private static async ValueTask PutNonSoapApi(WorkspaceApiUri uri, WorkspaceApiDto dto, HttpPipeline pipeline, CancellationToken cancellationToken)
{
// Put API without the specification.
var modelWithoutSpecification = dto with
{
Properties = dto.Properties with
{
Format = null,
Value = null,
ServiceUrl = null,
Type = dto.Properties.Type,
ApiType = dto.Properties.ApiType
}
};
await pipeline.PutContent(uri.ToUri(), BinaryData.FromObjectAsJson(modelWithoutSpecification), cancellationToken);
// Put API again with specification
await pipeline.PutContent(uri.ToUri(), BinaryData.FromObjectAsJson(dto), cancellationToken);
}
private static bool CreationInProgress(Response response)
{
if (response.Status != (int)HttpStatusCode.Created)
{
return false;
}
if (response.Headers.Any(header => header.Name.Equals("Content-Type", StringComparison.OrdinalIgnoreCase)
&& header.Value.Contains("application/json", StringComparison.OrdinalIgnoreCase)) is false)
{
return false;
}
try
{
return response.Content.ToObjectFromJson<JsonObject>()
.TryGetJsonObjectProperty("properties")
.Bind(json => json.TryGetStringProperty("ProvisioningState"))
.ToOption()
.Where(state => state.Equals("InProgress", StringComparison.OrdinalIgnoreCase))
.IsSome;
}
catch (JsonException)
{
return false;
}
}
public static async ValueTask PutGraphQlSchema(this WorkspaceApiUri uri, BinaryData schema, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var contents = BinaryData.FromObjectAsJson(new JsonObject()
{
["properties"] = new JsonObject
{
["contentType"] = "application/vnd.ms-azure-apim.graphql.schema",
["document"] = new JsonObject()
{
["value"] = schema.ToString()
}
}
});
await pipeline.PutContent(uri.ToUri()
.AppendPathSegment("schemas")
.AppendPathSegment("graphql")
.ToUri(), contents, cancellationToken);
}
public static async ValueTask<Option<BinaryData>> TryGetGraphQlSchema(this WorkspaceApiUri uri, HttpPipeline pipeline, CancellationToken cancellationToken)
{
var schemaUri = uri.ToUri()
.AppendPathSegment("schemas")
.AppendPathSegment("graphql")
.ToUri();
var schemaJsonOption = await pipeline.GetJsonObjectOption(schemaUri, cancellationToken);
return schemaJsonOption.Map(GetGraphQlSpecificationFromSchemaResponse);
}
private static BinaryData GetGraphQlSpecificationFromSchemaResponse(JsonObject responseJson)
{
var schema = responseJson.GetJsonObjectProperty("properties")
.GetJsonObjectProperty("document")
.GetNonEmptyOrWhiteSpaceStringProperty("value");
return BinaryData.FromString(schema);
}
public static IEnumerable<WorkspaceApiDirectory> ListDirectories(ManagementServiceDirectory serviceDirectory) =>
from workspaceDirectory in WorkspaceModule.ListDirectories(serviceDirectory)
let workspaceApisDirectory = new WorkspaceApisDirectory { Parent = workspaceDirectory }
where workspaceApisDirectory.ToDirectoryInfo().Exists()
from workspaceApiDirectoryInfo in workspaceApisDirectory.ToDirectoryInfo().ListDirectories("*")
let name = ApiName.From(workspaceApiDirectoryInfo.Name)
select new WorkspaceApiDirectory
{
Parent = workspaceApisDirectory,
Name = name
};
public static IEnumerable<WorkspaceApiInformationFile> ListInformationFiles(ManagementServiceDirectory serviceDirectory) =>
from workspaceApiDirectory in ListDirectories(serviceDirectory)
let informationFile = new WorkspaceApiInformationFile { Parent = workspaceApiDirectory }
where informationFile.ToFileInfo().Exists()
select informationFile;
public static async ValueTask WriteDto(this WorkspaceApiInformationFile file, WorkspaceApiDto dto, CancellationToken cancellationToken)
{
var content = BinaryData.FromObjectAsJson(dto, JsonObjectExtensions.SerializerOptions);
await file.ToFileInfo().OverwriteWithBinaryData(content, cancellationToken);
}
public static async ValueTask WriteSpecification(this WorkspaceApiSpecificationFile file, BinaryData contents, CancellationToken cancellationToken) =>
await file.ToFileInfo().OverwriteWithBinaryData(contents, cancellationToken);
public static async ValueTask<WorkspaceApiDto> ReadDto(this WorkspaceApiInformationFile file, CancellationToken cancellationToken)
{
var content = await file.ToFileInfo().ReadAsBinaryData(cancellationToken);
return content.ToObjectFromJson<WorkspaceApiDto>();
}
}