diff --git a/tools/code/publisher/Api.cs b/tools/code/publisher/Api.cs index 207c7c91..65f0f590 100644 --- a/tools/code/publisher/Api.cs +++ b/tools/code/publisher/Api.cs @@ -17,7 +17,7 @@ namespace publisher; internal static class Api { - public static async ValueTask ProcessDeletedArtifacts(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) + public static async ValueTask ProcessDeletedArtifacts(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, GetRestResource getRestResource, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) { var configurationApis = GetConfigurationApis(configurationJson); @@ -27,7 +27,7 @@ await GetApisFromFiles(files, serviceDirectory) secondKeySelector: configurationArtifact => configurationArtifact.ApiName, firstSelector: api => (api.ApiName, api.InformationFile, api.SpecificationFile, ConfigurationApiJson: (JsonObject?)null), bothSelector: (file, configurationArtifact) => (file.ApiName, file.InformationFile, file.SpecificationFile, ConfigurationApiJson: configurationArtifact.Json)) - .ForEachParallel(async artifact => await ProcessDeletedApi(artifact.ApiName, artifact.InformationFile, artifact.SpecificationFile, artifact.ConfigurationApiJson, serviceDirectory, serviceUri, putRestResource, deleteRestResource, logger, cancellationToken), + .ForEachParallel(async artifact => await ProcessDeletedApi(artifact.ApiName, artifact.InformationFile, artifact.SpecificationFile, artifact.ConfigurationApiJson, serviceDirectory, serviceUri, getRestResource, putRestResource, deleteRestResource, logger, cancellationToken), cancellationToken); } @@ -192,7 +192,7 @@ private static ApiName GetApiName(ApiSpecificationFile specificationFile) return new(specificationFile.ApiDirectory.GetName()); } - private static async ValueTask ProcessDeletedApi(ApiName apiName, ApiInformationFile? deletedApiInformationFile, ApiSpecificationFile? deletedSpecificationFile, JsonObject? configurationApiJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) + private static async ValueTask ProcessDeletedApi(ApiName apiName, ApiInformationFile? deletedApiInformationFile, ApiSpecificationFile? deletedSpecificationFile, JsonObject? configurationApiJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, GetRestResource getRestResource, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) { switch (deletedApiInformationFile, deletedSpecificationFile) { @@ -208,7 +208,7 @@ private static async ValueTask ProcessDeletedApi(ApiName apiName, ApiInformation } else { - await PutApi(apiName, existingInformationFile, specificationFile: null, configurationApiJson, serviceUri, putRestResource, logger, cancellationToken); + await PutApi(apiName, existingInformationFile, specificationFile: null, configurationApiJson, serviceUri, getRestResource, putRestResource, logger, cancellationToken); } return; @@ -221,7 +221,7 @@ private static async ValueTask ProcessDeletedApi(ApiName apiName, ApiInformation } else { - await PutApi(apiName, apiInformationFile: null, existingSpecificationFile, configurationApiJson, serviceUri, putRestResource, logger, cancellationToken); + await PutApi(apiName, apiInformationFile: null, existingSpecificationFile, configurationApiJson, serviceUri, getRestResource, putRestResource, logger, cancellationToken); } return; @@ -260,7 +260,7 @@ public static ApiUri GetApiUri(ApiName apiName, ServiceUri serviceUri) .FirstOrDefault() : null; } - private static async ValueTask PutApi(ApiName apiName, ApiInformationFile? apiInformationFile, ApiSpecificationFile? specificationFile, JsonObject? configurationApiJson, ServiceUri serviceUri, PutRestResource putRestResource, ILogger logger, CancellationToken cancellationToken) + private static async ValueTask PutApi(ApiName apiName, ApiInformationFile? apiInformationFile, ApiSpecificationFile? specificationFile, JsonObject? configurationApiJson, ServiceUri serviceUri, GetRestResource getRestResource, PutRestResource putRestResource, ILogger logger, CancellationToken cancellationToken) { if (apiInformationFile is null && specificationFile is null && configurationApiJson is null) { @@ -278,7 +278,7 @@ private static async ValueTask PutApi(ApiName apiName, ApiInformationFile? apiIn putUri = putUri.SetQueryParam("import", "true").ToUri(); } - var apiJson = await GetApiJson(apiName, apiInformationFile, specificationFile, configurationApiJson, cancellationToken); + var apiJson = await GetApiJson(apiName, apiInformationFile, specificationFile, configurationApiJson, serviceUri, getRestResource, cancellationToken); await putRestResource(putUri, apiJson, cancellationToken); // Handle GraphQL specification @@ -288,7 +288,7 @@ private static async ValueTask PutApi(ApiName apiName, ApiInformationFile? apiIn } } - private static async ValueTask GetApiJson(ApiName apiName, ApiInformationFile? apiInformationFile, ApiSpecificationFile? specificationFile, JsonObject? configurationApiJson, CancellationToken cancellationToken) + private static async ValueTask GetApiJson(ApiName apiName, ApiInformationFile? apiInformationFile, ApiSpecificationFile? specificationFile, JsonObject? configurationApiJson, ServiceUri serviceUri, GetRestResource getRestResource, CancellationToken cancellationToken) { var apiJson = new JsonObject(); @@ -301,6 +301,13 @@ private static async ValueTask GetApiJson(ApiName apiName, ApiInform if (specificationFile is not null and (ApiSpecificationFile.Wadl or ApiSpecificationFile.Wsdl or ApiSpecificationFile.OpenApi)) { var specificationJson = await GetApiSpecificationJson(specificationFile, cancellationToken); + //If there is not apiInformationFile, retrieve the serviceUrl from the API's service. + if (apiInformationFile is null) + { + // Temporary work around to address the fact the management API will update the serviceURL to the management API's URL. + var propertyName = "serviceUrl"; + apiJson = await GetPropertiesPropertyFronJson(apiName, propertyName, serviceUri, getRestResource, cancellationToken); + } apiJson = apiJson.Merge(specificationJson); } @@ -317,6 +324,52 @@ private static async ValueTask GetApiJson(ApiName apiName, ApiInform return apiJson; } + /// + /// This method will call the REST endpoint for the passed in API Name. It then will take that response, parse the properties object + /// and return the propertyName property. + /// + /// Name of the api + /// Name of the property you want to get the value of from the properties object + /// URI of the endpoint you need to get the json from + /// Delegate to call the REST endpoint based on the apiName + /// Cancellation Token + /// + static async Task GetPropertiesPropertyFronJson(ApiName apiName, String propertyName, ServiceUri serviceUri, GetRestResource getRestResource, CancellationToken cancellationToken) + { + // If the API does not have an information file, retrieve the serviceUrl from the API's service. + var apiUri = GetApiUri(apiName, serviceUri); + var apiJsonObject = await getRestResource(apiUri.Uri, cancellationToken); + var propertiesNode = apiJsonObject.GetNullableProperty("properties"); + + if (propertiesNode != null && propertiesNode is JsonObject propertiesObject) + { + var apiServiceUrlNode = propertiesObject.GetNullableProperty(propertyName); + + if (apiServiceUrlNode != null && apiServiceUrlNode is JsonValue apiServiceUrlValue) + { + var apiServiceUrl = apiServiceUrlValue.ToString(); + + JsonObject apiJson = new JsonObject + { + ["properties"] = new JsonObject + { + ["serviceUrl"] = apiServiceUrl + } + }; + + return apiJson; + } + else + { + throw new InvalidOperationException($"Could not find property '{propertyName}' in the properties object of the JSON object."); + } + } + else + { + throw new InvalidOperationException($"Could not find the properties object in JSON object."); + } + } + private static async ValueTask GetApiSpecificationJson(ApiSpecificationFile specificationFile, CancellationToken cancellationToken) { var json = new JsonObject @@ -378,7 +431,7 @@ private static async ValueTask PutGraphQlSchema(ApiUri apiUri, ApiSpecificationF await putRestResource(schemaUri.Uri, json, cancellationToken); } - public static async ValueTask ProcessArtifactsToPut(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, PutRestResource putRestResource, ILogger logger, CancellationToken cancellationToken) + public static async ValueTask ProcessArtifactsToPut(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, GetRestResource getRestResource, PutRestResource putRestResource, ILogger logger, CancellationToken cancellationToken) { var configurationApis = GetConfigurationApis(configurationJson); @@ -388,7 +441,7 @@ await GetApisFromFiles(files, serviceDirectory) secondKeySelector: configurationArtifact => configurationArtifact.ApiName, firstSelector: api => (api.ApiName, api.InformationFile, api.SpecificationFile, ConfigurationApiJson: (JsonObject?)null), bothSelector: (fileApi, configurationArtifact) => (fileApi.ApiName, fileApi.InformationFile, fileApi.SpecificationFile, ConfigurationApiJson: configurationArtifact.Json)) - .ForEachParallel(async api => await PutApi(api.ApiName, api.InformationFile, api.SpecificationFile, api.ConfigurationApiJson, serviceUri, putRestResource, logger, cancellationToken), + .ForEachParallel(async api => await PutApi(api.ApiName, api.InformationFile, api.SpecificationFile, api.ConfigurationApiJson, serviceUri, getRestResource, putRestResource, logger, cancellationToken), cancellationToken); } -} \ No newline at end of file +} diff --git a/tools/code/publisher/Common.cs b/tools/code/publisher/Common.cs index f97fb5ef..347a23df 100644 --- a/tools/code/publisher/Common.cs +++ b/tools/code/publisher/Common.cs @@ -7,6 +7,7 @@ namespace publisher; internal delegate IAsyncEnumerable ListRestResources(Uri uri, CancellationToken cancellationToken); +internal delegate ValueTask GetRestResource(Uri uri, CancellationToken cancellationToken); internal delegate ValueTask PutRestResource(Uri uri, JsonObject jsonObject, CancellationToken cancellationToken); diff --git a/tools/code/publisher/Program.cs b/tools/code/publisher/Program.cs index 6389faed..0091772f 100644 --- a/tools/code/publisher/Program.cs +++ b/tools/code/publisher/Program.cs @@ -51,6 +51,7 @@ private static void ConfigureServices(IServiceCollection services) .AddSingleton(GetHttpPipeline) .AddSingleton(GetDeleteRestResource) .AddSingleton(GetListRestResources) + .AddSingleton(GetGetRestResource) .AddSingleton(GetPutRestResource) .AddSingleton(GetPublisherParameters) .AddHostedService(); @@ -149,6 +150,30 @@ private static ListRestResources GetListRestResources(IServiceProvider provider) }; } + private static GetRestResource GetGetRestResource(IServiceProvider provider) + { + var pipeline = provider.GetRequiredService(); + var logger = provider.GetRequiredService().CreateLogger(nameof(GetRestResource)); + + return async (uri, cancellationToken) => + { + logger.LogDebug("Beginning request to get REST resource at URI {uri}...", uri); + + var json = await pipeline.GetJsonObject(uri, cancellationToken); + + if (logger.IsEnabled(LogLevel.Trace)) + { + logger.LogTrace("Successfully retrieved REST resource {json} at URI {uri}.", json.ToJsonString(), uri); + } + else + { + logger.LogDebug("Successfully retrieved REST resource at URI {uri}.", uri); + } + + return json; + }; + } + private static PutRestResource GetPutRestResource(IServiceProvider provider) { var pipeline = provider.GetRequiredService(); @@ -184,6 +209,7 @@ private static Publisher.Parameters GetPublisherParameters(IServiceProvider prov ConfigurationJson = GetConfigurationJson(configuration), DeleteRestResource = provider.GetRequiredService(), ListRestResources = provider.GetRequiredService(), + GetRestResource = provider.GetRequiredService(), Logger = provider.GetRequiredService().CreateLogger(nameof(Publisher)), PutRestResource = provider.GetRequiredService(), ServiceDirectory = GetServiceDirectory(configuration), diff --git a/tools/code/publisher/Publisher.cs b/tools/code/publisher/Publisher.cs index c1e9bd4b..00341fa4 100644 --- a/tools/code/publisher/Publisher.cs +++ b/tools/code/publisher/Publisher.cs @@ -24,6 +24,7 @@ internal record Parameters public required DeleteRestResource DeleteRestResource { get; init; } public required ILogger Logger { get; init; } public required ListRestResources ListRestResources { get; init; } + public required GetRestResource GetRestResource { get; init; } public required PutRestResource PutRestResource { get; init; } public required ServiceDirectory ServiceDirectory { get; init; } public required ServiceUri ServiceUri { get; init; } @@ -86,6 +87,7 @@ await Service.ProcessArtifactsToPut(files, publisherParameters.ServiceDirectory, publisherParameters.ServiceUri, publisherParameters.ListRestResources, + publisherParameters.GetRestResource, publisherParameters.PutRestResource, publisherParameters.DeleteRestResource, logger, @@ -141,6 +143,7 @@ await Service.ProcessDeletedArtifacts(deletedCommitIdFiles, publisherParameters.ServiceDirectory, publisherParameters.ServiceUri, publisherParameters.ListRestResources, + publisherParameters.GetRestResource, publisherParameters.PutRestResource, publisherParameters.DeleteRestResource, publisherParameters.Logger, @@ -154,6 +157,7 @@ await Service.ProcessArtifactsToPut(commitIdFilesToPut, publisherParameters.ServiceDirectory, publisherParameters.ServiceUri, publisherParameters.ListRestResources, + publisherParameters.GetRestResource, publisherParameters.PutRestResource, publisherParameters.DeleteRestResource, publisherParameters.Logger, diff --git a/tools/code/publisher/Service.cs b/tools/code/publisher/Service.cs index 47c8c640..80579177 100644 --- a/tools/code/publisher/Service.cs +++ b/tools/code/publisher/Service.cs @@ -10,7 +10,7 @@ namespace publisher; internal static class Service { - public static async ValueTask ProcessDeletedArtifacts(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, ListRestResources listRestResources, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) + public static async ValueTask ProcessDeletedArtifacts(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, ListRestResources listRestResources, GetRestResource getRestResource, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) { await GatewayApi.ProcessDeletedArtifacts(files, configurationJson, serviceDirectory, serviceUri, listRestResources, putRestResource, deleteRestResource, logger, cancellationToken); await ProductApi.ProcessDeletedArtifacts(files, configurationJson, serviceDirectory, serviceUri, listRestResources, putRestResource, deleteRestResource, logger, cancellationToken); @@ -18,7 +18,7 @@ public static async ValueTask ProcessDeletedArtifacts(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, ListRestResources listRestResources, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) + public static async ValueTask ProcessArtifactsToPut(IReadOnlyCollection files, JsonObject configurationJson, ServiceDirectory serviceDirectory, ServiceUri serviceUri, ListRestResources listRestResources, GetRestResource getRestResource, PutRestResource putRestResource, DeleteRestResource deleteRestResource, ILogger logger, CancellationToken cancellationToken) { await NamedValue.ProcessArtifactsToPut(files, configurationJson, serviceDirectory, serviceUri, putRestResource, logger, cancellationToken); await Tag.ProcessArtifactsToPut(files, configurationJson, serviceDirectory, serviceUri, putRestResource, logger, cancellationToken); @@ -50,7 +50,7 @@ public static async ValueTask ProcessArtifactsToPut(IReadOnlyCollection