diff --git a/src/ArmTemplates/Common/Constants/ApiTypeConstants.cs b/src/ArmTemplates/Common/Constants/ApiTypeConstants.cs new file mode 100644 index 00000000..76520463 --- /dev/null +++ b/src/ArmTemplates/Common/Constants/ApiTypeConstants.cs @@ -0,0 +1,15 @@ +// -------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// -------------------------------------------------------------------------- + +namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Constants +{ + public static class ApiTypeConstants + { + public const string WebSocket = "websocket"; + public const string Graphql = "graphql"; + public const string Http = "http"; + public const string Soap = "soap"; + } +} \ No newline at end of file diff --git a/src/ArmTemplates/Extractor/EntityExtractors/APIExtractor.cs b/src/ArmTemplates/Extractor/EntityExtractors/APIExtractor.cs index d558283e..063b61f2 100644 --- a/src/ArmTemplates/Extractor/EntityExtractors/APIExtractor.cs +++ b/src/ArmTemplates/Extractor/EntityExtractors/APIExtractor.cs @@ -112,6 +112,7 @@ public async Task GenerateSingleApiTemplateResourcesAsync( this.logger.LogInformation("{0} API data found ...", singleApiName); this.SetArmTemplateValuesToApiTemplateResource(singleApiName, apiResource, extractorParameters, apiDepends); + this.SanitizeOutputApiTemplateResource(singleApiName, apiResource, apiTemplateResources); apiTemplateResources.Apis.Add(apiResource); } catch (Exception ex) @@ -279,5 +280,32 @@ async Task> GenerateApiRevisionsDependenciesAsync(Ext return apiDependency; } + + void SanitizeOutputApiTemplateResource(string apiName, ApiTemplateResource apiResource, ApiTemplateResources apiTemplateResources) + { + //remove api operation if websocket type & change dependencies to api, since api operation for websocket type is created automatically and it is not allowed to update it (built-in operation) + if (apiResource.Properties.Type == ApiTypeConstants.WebSocket) + { + if (!apiTemplateResources.ApiOperations.IsNullOrEmpty()) + { + if (!apiTemplateResources.ApiOperationsPolicies.IsNullOrEmpty()) + { + foreach (var apiOperationPolicy in apiTemplateResources.ApiOperationsPolicies) + { + apiOperationPolicy.DependsOn = new string[] { NamingHelper.GenerateApisResourceId(apiName) }; + } + } + + if (!apiTemplateResources.ApiOperationsTags.IsNullOrEmpty()) + { + foreach (var apiOperationTag in apiTemplateResources.ApiOperationsTags) + { + apiOperationTag.DependsOn = new string[] { NamingHelper.GenerateApisResourceId(apiName) }; + } + } + apiTemplateResources.ApiOperations.Clear(); + } + } + } } } diff --git a/tests/ArmTemplates.Tests/Extractor/Scenarios/ApiExtractorTests.cs b/tests/ArmTemplates.Tests/Extractor/Scenarios/ApiExtractorTests.cs index 550ede74..c2273b0d 100644 --- a/tests/ArmTemplates.Tests/Extractor/Scenarios/ApiExtractorTests.cs +++ b/tests/ArmTemplates.Tests/Extractor/Scenarios/ApiExtractorTests.cs @@ -102,7 +102,7 @@ public async Task GenerateApiTemplates_ProperlyLaysTheInformation() // assert File.Exists(Path.Combine(currentTestDirectory, apiTemplate.TypedResources.FileName)).Should().BeTrue(); - Directory.GetFiles(Path.Combine(currentTestDirectory, PolicyExtractor.PoliciesDirectoryName)).Count().Should().Be(4); + Directory.GetFiles(Path.Combine(currentTestDirectory, PolicyExtractor.PoliciesDirectoryName)).Count().Should().Be(6); apiTemplate.Parameters.Should().NotBeNull(); apiTemplate.Parameters.Should().ContainKey(ParameterNames.ApimServiceName); @@ -110,34 +110,34 @@ public async Task GenerateApiTemplates_ProperlyLaysTheInformation() apiTemplate.Parameters.Should().ContainKey(ParameterNames.ApiLoggerId); apiTemplate.Parameters.Should().ContainKey(ParameterNames.PolicyXMLBaseUrl); apiTemplate.Parameters.Should().ContainKey(ParameterNames.PolicyXMLSasToken); - apiTemplate.Resources.Count().Should().Be(23); + apiTemplate.Resources.Count().Should().Be(33); // apis - apiTemplate.TypedResources.Apis.Count().Should().Be(2); + apiTemplate.TypedResources.Apis.Count().Should().Be(3); apiTemplate.TypedResources.Apis.All(x => x.Type == ResourceTypeConstants.API).Should().BeTrue(); apiTemplate.TypedResources.Apis.All(x => x.Properties is not null).Should().BeTrue(); // api schemas - apiTemplate.TypedResources.ApiSchemas.Count().Should().Be(2); + apiTemplate.TypedResources.ApiSchemas.Count().Should().Be(3); apiTemplate.TypedResources.ApiSchemas.All(x => x.Type == ResourceTypeConstants.APISchema).Should().BeTrue(); apiTemplate.TypedResources.ApiSchemas.All(x => x.Properties is not null).Should().BeTrue(); // diagnostics - apiTemplate.TypedResources.Diagnostics.Count().Should().Be(3); + apiTemplate.TypedResources.Diagnostics.Count().Should().Be(4); apiTemplate.TypedResources.Diagnostics.All(x => x.Type == ResourceTypeConstants.APIServiceDiagnostic || x.Type == ResourceTypeConstants.APIDiagnostic).Should().BeTrue(); apiTemplate.TypedResources.Diagnostics.All(x => x.Properties is not null).Should().BeTrue(); // tags - apiTemplate.TypedResources.Tags.Count().Should().Be(4); - apiTemplate.TypedResources.Tags.All(x => x.Type == ResourceTypeConstants.ProductTag).Should().BeTrue(); + apiTemplate.TypedResources.Tags.Count().Should().Be(6); + apiTemplate.TypedResources.Tags.All(x => x.Type == ResourceTypeConstants.APITag).Should().BeTrue(); // api products - apiTemplate.TypedResources.ApiProducts.Count().Should().Be(2); + apiTemplate.TypedResources.ApiProducts.Count().Should().Be(3); apiTemplate.TypedResources.ApiProducts.All(x => x.Type == ResourceTypeConstants.ProductApi).Should().BeTrue(); apiTemplate.TypedResources.ApiProducts.All(x => x.Properties is not null).Should().BeTrue(); // api policies - apiTemplate.TypedResources.ApiPolicies.Count().Should().Be(2); + apiTemplate.TypedResources.ApiPolicies.Count().Should().Be(3); apiTemplate.TypedResources.ApiPolicies.All(x => x.Properties is not null).Should().BeTrue(); // api operations @@ -152,12 +152,12 @@ public async Task GenerateApiTemplates_ProperlyLaysTheInformation() apiTemplate.TypedResources.ApiOperations.All(x => x.Properties.Request.Representations.All(o => o.Examples.ContainsKey("default"))).Should().BeTrue(); // api operations policies - apiTemplate.TypedResources.ApiOperationsPolicies.Count().Should().Be(2); + apiTemplate.TypedResources.ApiOperationsPolicies.Count().Should().Be(3); apiTemplate.TypedResources.ApiOperations.All(x => x.Properties is not null).Should().BeTrue(); // api operations tags - apiTemplate.TypedResources.ApiOperationsPolicies.Count().Should().Be(2); - apiTemplate.TypedResources.ApiOperations.All(x => x.Properties is not null).Should().BeTrue(); + apiTemplate.TypedResources.ApiOperationsTags.Count().Should().Be(6); + apiTemplate.TypedResources.ApiOperationsTags.All(x => x.Type == ResourceTypeConstants.APIOperationTag).Should().BeTrue(); } [Fact] @@ -227,11 +227,76 @@ public async Task GenerateGraphQLApiTemplates() string schemaContentType = "application/vnd.ms-azure-apim.graphql.schema"; // api schemas - apiTemplate.TypedResources.ApiSchemas.Count().Should().Be(2); + apiTemplate.TypedResources.ApiSchemas.Count().Should().Be(3); apiTemplate.TypedResources.ApiSchemas.All(x => x.Type == ResourceTypeConstants.APISchema).Should().BeTrue(); apiTemplate.TypedResources.ApiSchemas.All(x => x.Properties is not null).Should().BeTrue(); apiTemplate.TypedResources.ApiSchemas.All(x => x.Properties.Document.Value.ToString().Equals(fileReadingTask.Result.ToString())).Should().BeTrue(); apiTemplate.TypedResources.ApiSchemas.All(x => x.Properties.ContentType.Equals(schemaContentType)).Should().BeTrue(); } + + [Fact] + public async Task GenerateApiTemplateAsync_WebsocketApiTypeOperationsNotGenerated() + { + // arrange + var currentTestDirectory = Path.Combine(this.OutputDirectory, nameof(GenerateApiTemplateAsync_WebsocketApiTypeOperationsNotGenerated)); + + var extractorConfig = this.GetDefaultExtractorConsoleAppConfiguration( + sourceApimName: string.Empty, + destinationApimName: string.Empty, + resourceGroup: string.Empty, + fileFolder: string.Empty, + apiName: string.Empty); + var extractorParameters = new ExtractorParameters(extractorConfig); + + // mocked clients + var mockedApiClient = MockApisClient.GetMockedApiClientWithDefaultValues(); + var mockedProductClient = MockProductsClient.GetMockedApiClientWithDefaultValues(); + var mockedApiSchemaClient = MockApiSchemaClient.GetMockedApiClientWithGraphQLSchemaValues(); + var mockedPolicyClient = MockPolicyClient.GetMockedApiClientWithDefaultValues(); + var mockedTagClient = MockTagClient.GetMockedApiClientWithDefaultValues(); + var mockedApiOperationClient = MockApiOperationClient.GetMockedApiClientWithDefaultValues(); + var mockedDiagnosticClient = MockDiagnosticClient.GetMockedClientWithApiDependentValues(); + var mockedRevisionClient = MockApisRevisionsClient.GetMockedApiRevisionClientWithDefaultValues(); + + // mocked extractors + var mockedDiagnosticExtractor = new DiagnosticExtractor(this.GetTestLogger(), mockedDiagnosticClient); + var mockedApiSchemaExtractor = new ApiSchemaExtractor(this.GetTestLogger(), mockedApiSchemaClient); + var mockedPolicyExtractor = new PolicyExtractor(this.GetTestLogger(), mockedPolicyClient, new TemplateBuilder()); + var mockedProductApisExtractor = new ProductApisExtractor(this.GetTestLogger(), mockedProductClient, mockedApiClient, new TemplateBuilder()); + var mockedTagExtractor = new TagExtractor(this.GetTestLogger(), mockedTagClient, new TemplateBuilder()); + var mockedApiOperationExtractor = new ApiOperationExtractor(this.GetTestLogger(), mockedApiOperationClient); + + var apiExtractor = new ApiExtractor( + this.GetTestLogger(), + new TemplateBuilder(), + mockedApiClient, + mockedDiagnosticExtractor, + mockedApiSchemaExtractor, + mockedPolicyExtractor, + mockedProductApisExtractor, + mockedTagExtractor, + mockedApiOperationExtractor, + mockedRevisionClient); + + var extractorExecutor = ExtractorExecutor.BuildExtractorExecutor( + this.GetTestLogger(), + apiExtractor: apiExtractor); + extractorExecutor.SetExtractorParameters(extractorParameters); + + // act + var apiTemplate = await extractorExecutor.GenerateApiTemplateAsync( + singleApiName: It.IsAny(), + multipleApiNames: It.IsAny>(), + currentTestDirectory); + + // assert + File.Exists(Path.Combine(currentTestDirectory, apiTemplate.TypedResources.FileName)).Should().BeTrue(); + + // api operation resources + apiTemplate.TypedResources.ApiOperations.Count().Should().Be(2); + apiTemplate.TypedResources.ApiOperations.Any(x => x.Name.Contains("websocket-api")).Should().BeFalse(); + apiTemplate.TypedResources.ApiOperationsTags.Count().Should().Be(6); + apiTemplate.TypedResources.ApiOperationsPolicies.Count().Should().Be(3); + } } } diff --git a/tests/ArmTemplates.Tests/Extractor/Scenarios/ProductApisExtractorTests.cs b/tests/ArmTemplates.Tests/Extractor/Scenarios/ProductApisExtractorTests.cs index 64a1e0df..12c6d5d6 100644 --- a/tests/ArmTemplates.Tests/Extractor/Scenarios/ProductApisExtractorTests.cs +++ b/tests/ArmTemplates.Tests/Extractor/Scenarios/ProductApisExtractorTests.cs @@ -64,8 +64,8 @@ public async Task GenerateProductApisTemplates_ProperlyLaysTheInformation() File.Exists(Path.Combine(currentTestDirectory, extractorParameters.FileNames.ProductAPIs)).Should().BeTrue(); productApisTemplate.Parameters.Should().ContainKey(ParameterNames.ApimServiceName); - productApisTemplate.TypedResources.ProductApis.Count().Should().Be(2); - productApisTemplate.Resources.Count().Should().Be(2); + productApisTemplate.TypedResources.ProductApis.Count().Should().Be(3); + productApisTemplate.Resources.Count().Should().Be(3); foreach (var productApi in productApisTemplate.TypedResources.ProductApis) { diff --git a/tests/ArmTemplates.Tests/Extractor/Scenarios/TagApiExtractorTests.cs b/tests/ArmTemplates.Tests/Extractor/Scenarios/TagApiExtractorTests.cs index 981e8911..7fc6259c 100644 --- a/tests/ArmTemplates.Tests/Extractor/Scenarios/TagApiExtractorTests.cs +++ b/tests/ArmTemplates.Tests/Extractor/Scenarios/TagApiExtractorTests.cs @@ -64,8 +64,8 @@ public async Task GenerateTagApiTemplates_ProperlyLaysTheInformation() File.Exists(Path.Combine(currentTestDirectory, extractorParameters.FileNames.TagApi)).Should().BeTrue(); tagApiTemplate.Parameters.Should().ContainKey(ParameterNames.ApimServiceName); - tagApiTemplate.TypedResources.Tags.Count().Should().Be(4); - tagApiTemplate.Resources.Count().Should().Be(4); + tagApiTemplate.TypedResources.Tags.Count().Should().Be(6); + tagApiTemplate.Resources.Count().Should().Be(6); var resources = tagApiTemplate.TypedResources; diff --git a/tests/ArmTemplates.Tests/Moqs/ApiClients/MockApisClient.cs b/tests/ArmTemplates.Tests/Moqs/ApiClients/MockApisClient.cs index ec29f64b..c82cf012 100644 --- a/tests/ArmTemplates.Tests/Moqs/ApiClients/MockApisClient.cs +++ b/tests/ArmTemplates.Tests/Moqs/ApiClients/MockApisClient.cs @@ -20,6 +20,7 @@ public class MockApisClient public const string ServiceApiName1 = "api-name-1"; public const string ServiceApiName2 = "api-name-2"; + public const string ServiceApiName3 = "websocket-api"; public static ApiProperties GetMockedServiceApiProperties2() { @@ -59,12 +60,28 @@ public static ApiProperties GetMockedServiceApiProperties1() }; } + public static ApiProperties GetMockedServiceApiPropertiesWebsocket() + { + return new ApiProperties + { + DisplayName = "websocket-api-display-name-3", + ApiRevision = "1", + Description = "api-description-3", + SubscriptionRequired = true, + ServiceUrl = "ws://host", + Type = "websocket", + Path = "path-3", + IsCurrent = true, + }; + } + public static IApisClient GetMockedApiClientWithDefaultValues() { var mockedApisClient = new Mock(MockBehavior.Strict); var serviceProperties1 = GetMockedServiceApiProperties1(); var serviceProperties2 = GetMockedServiceApiProperties2(); + var serviceProperties3 = GetMockedServiceApiPropertiesWebsocket(); mockedApisClient .Setup(x => x.GetAllAsync(It.IsAny())) @@ -83,6 +100,13 @@ public static IApisClient GetMockedApiClientWithDefaultValues() Type = TemplateType, Properties = serviceProperties2 }, + + new ApiTemplateResource + { + Name = ServiceApiName3, + Type = TemplateType, + Properties = serviceProperties3 + }, }); mockedApisClient @@ -128,6 +152,15 @@ public static IApisClient GetMockedApiClientWithDefaultValues() Properties = serviceProperties2 }); + mockedApisClient + .Setup(x => x.GetSingleAsync(It.Is((o => o.Equals(ServiceApiName3))), It.IsAny())) + .ReturnsAsync((string _, ExtractorParameters _) => new ApiTemplateResource + { + Name = ServiceApiName3, + Type = TemplateType, + Properties = serviceProperties3 + }); + mockedApisClient .Setup(x => x.GetAllLinkedToProductAsync(It.IsAny(), It.IsAny())) .ReturnsAsync((string productName, ExtractorParameters _) => new List @@ -144,6 +177,13 @@ public static IApisClient GetMockedApiClientWithDefaultValues() Name = ServiceApiName2, Type = TemplateType, Properties = serviceProperties2 + }, + + new ApiTemplateResource + { + Name = ServiceApiName3, + Type = TemplateType, + Properties = serviceProperties3 } }); diff --git a/tests/ArmTemplates.Tests/Moqs/ApiClients/MockTagClient.cs b/tests/ArmTemplates.Tests/Moqs/ApiClients/MockTagClient.cs index 57128c1b..1f023dd5 100644 --- a/tests/ArmTemplates.Tests/Moqs/ApiClients/MockTagClient.cs +++ b/tests/ArmTemplates.Tests/Moqs/ApiClients/MockTagClient.cs @@ -30,13 +30,13 @@ public static ITagClient GetMockedApiClientWithDefaultValues() new TagTemplateResource { Name = TagName1, - Type = ResourceTypeConstants.ProductTag + Type = ResourceTypeConstants.APIOperationTag }, new TagTemplateResource { Name = TagName2, - Type = ResourceTypeConstants.ProductTag + Type = ResourceTypeConstants.APIOperationTag } }); @@ -47,13 +47,13 @@ public static ITagClient GetMockedApiClientWithDefaultValues() new TagTemplateResource { Name = TagName1, - Type = ResourceTypeConstants.ProductTag + Type = ResourceTypeConstants.APITag }, new TagTemplateResource { Name = TagName2, - Type = ResourceTypeConstants.ProductTag + Type = ResourceTypeConstants.APITag } }); @@ -64,13 +64,13 @@ public static ITagClient GetMockedApiClientWithDefaultValues() new TagTemplateResource { Name = TagName1, - Type = ResourceTypeConstants.APITag + Type = ResourceTypeConstants.ProductTag }, new TagTemplateResource { Name = TagName2, - Type = ResourceTypeConstants.APITag + Type = ResourceTypeConstants.ProductTag } });