in tools/code/publisher/WorkspaceApi.cs [139:260]
private static PutWorkspaceApi GetPutWorkspaceApi(IServiceProvider provider)
{
var findDto = provider.GetRequiredService<FindWorkspaceApiDto>();
var findSpecificationContents = provider.GetRequiredService<FindWorkspaceApiSpecificationContents>();
var correctRevisionNumber = provider.GetRequiredService<CorrectWorkspaceApimRevisionNumber>();
var putInApim = provider.GetRequiredService<PutWorkspaceApiInApim>();
var activitySource = provider.GetRequiredService<ActivitySource>();
var taskDictionary = new ConcurrentDictionary<(ApiName, WorkspaceName), AsyncLazy<Unit>>();
return putApi;
async ValueTask putApi(ApiName name, WorkspaceName workspaceName, CancellationToken cancellationToken)
{
using var _ = activitySource.StartActivity(nameof(PutWorkspaceApi))
?.AddTag("workspace.name", workspaceName)
?.AddTag("workspace_api.name", name);
await taskDictionary.GetOrAdd((name, workspaceName),
(pair) => new AsyncLazy<Unit>(async cancellationToken =>
{
var (name, workspaceName) = pair;
await putApiInner(name, workspaceName, cancellationToken);
return Unit.Default;
}))
.WithCancellation(cancellationToken);
};
async ValueTask putApiInner(ApiName name, WorkspaceName workspaceName, CancellationToken cancellationToken)
{
var informationFileDtoOption = await findDto(name, workspaceName, cancellationToken);
await informationFileDtoOption.IterTask(async informationFileDto =>
{
await putCurrentRevision(name, informationFileDto, workspaceName, cancellationToken);
var specificationContentsOption = await findSpecificationContents(name, workspaceName, cancellationToken);
var dto = await tryGetDto(name, informationFileDto, specificationContentsOption, cancellationToken);
var graphQlSpecificationContentsOption = specificationContentsOption.Bind(specificationContents =>
{
var (specification, contents) = specificationContents;
return specification is ApiSpecification.GraphQl graphQl
? (graphQl, contents)
: Option<(ApiSpecification.GraphQl, BinaryData)>.None;
});
await putInApim(name, dto, graphQlSpecificationContentsOption, workspaceName, cancellationToken);
});
}
async ValueTask putCurrentRevision(ApiName name, WorkspaceApiDto dto, WorkspaceName workspaceName, CancellationToken cancellationToken)
{
if (ApiName.IsRevisioned(name))
{
var rootName = ApiName.GetRootName(name);
await putApi(rootName, workspaceName, cancellationToken);
}
else
{
await correctRevisionNumber(name, dto, workspaceName, cancellationToken);
}
}
async ValueTask<WorkspaceApiDto> tryGetDto(ApiName name,
WorkspaceApiDto informationFileDto,
Option<(ApiSpecification, BinaryData)> specificationContentsOption,
CancellationToken cancellationToken)
{
var dto = informationFileDto;
await specificationContentsOption.IterTask(async specificationContents =>
{
var (specification, contents) = specificationContents;
dto = await addSpecificationToDto(name, dto, specification, contents, cancellationToken);
});
return dto;
}
static async ValueTask<WorkspaceApiDto> addSpecificationToDto(ApiName name, WorkspaceApiDto dto, ApiSpecification specification, BinaryData contents, CancellationToken cancellationToken) =>
dto with
{
Properties = dto.Properties with
{
Format = specification switch
{
ApiSpecification.Wsdl => "wsdl",
ApiSpecification.Wadl => "wadl-xml",
ApiSpecification.OpenApi openApi => (openApi.Format, openApi.Version) switch
{
(common.OpenApiFormat.Json, OpenApiVersion.V2) => "swagger-json",
(common.OpenApiFormat.Json, OpenApiVersion.V3) => "openapi+json",
(common.OpenApiFormat.Yaml, OpenApiVersion.V2) => "openapi",
(common.OpenApiFormat.Yaml, OpenApiVersion.V3) => "openapi",
_ => throw new InvalidOperationException($"Unsupported OpenAPI format '{openApi.Format}' and version '{openApi.Version}'.")
},
_ => dto.Properties.Format
},
// APIM does not support OpenAPI V2 YAML. Convert to V3 YAML if needed.
Value = specification switch
{
ApiSpecification.GraphQl => null,
ApiSpecification.OpenApi { Format: common.OpenApiFormat.Yaml, Version: OpenApiVersion.V2 } =>
await convertStreamToOpenApiV3Yaml(contents, $"Could not convert specification for API {name} to OpenAPIV3.", cancellationToken),
_ => contents.ToString()
}
}
};
static async ValueTask<string> convertStreamToOpenApiV3Yaml(BinaryData contents, string errorMessage, CancellationToken cancellationToken)
{
using var stream = contents.ToStream();
var readResult = await new OpenApiStreamReader().ReadAsync(stream, cancellationToken);
return readResult.OpenApiDiagnostic.Errors switch
{
[] => readResult.OpenApiDocument.Serialize(OpenApiSpecVersion.OpenApi3_0, Microsoft.OpenApi.OpenApiFormat.Yaml),
var errors => throw openApiErrorsToException(errorMessage, errors)
};
}
static OpenApiException openApiErrorsToException(string message, IEnumerable<OpenApiError> errors) =>
new($"{message}. Errors are: {Environment.NewLine}{string.Join(Environment.NewLine, errors)}");
}