Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit a056ef0

Browse files
f-alizadaFarhad Alizada
andauthored
fix: Extractor/exclude product api from apis template (#856)
* Remove api-products resources from apis template * Add product api extraction tests * Add product extractor tests for two cases. apiName null or non-empty Co-authored-by: Farhad Alizada <[email protected]>
1 parent 6f0a809 commit a056ef0

18 files changed

+387
-47
lines changed

src/ArmTemplates/Commands/Executors/ExtractorExecutor.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,12 @@ await FileWriter.SaveAsJsonAsync(
323323
/// <returns>generated products template</returns>
324324
public async Task<Template<ProductTemplateResources>> GenerateProductsTemplateAsync(
325325
string singleApiName,
326-
string baseFilesGenerationDirectory,
327-
List<ProductApiTemplateResource> productApiResources)
326+
string baseFilesGenerationDirectory)
328327
{
329328
this.logger.LogInformation("Started generation of products template...");
330329

331330
var productTemplate = await this.productExtractor.GenerateProductsTemplateAsync(
332-
singleApiName, productApiResources, baseFilesGenerationDirectory, this.extractorParameters);
331+
singleApiName, baseFilesGenerationDirectory, this.extractorParameters);
333332

334333
if (productTemplate?.HasResources() == true)
335334
{
@@ -1164,7 +1163,7 @@ async Task GenerateTemplates(
11641163

11651164
var globalServicePolicyTemplate = await this.GeneratePolicyTemplateAsync(baseFilesGenerationDirectory);
11661165
var productApiTemplate = await this.GenerateProductApisTemplateAsync(singleApiName, multipleApiNames, baseFilesGenerationDirectory);
1167-
var productTemplate = await this.GenerateProductsTemplateAsync(singleApiName, baseFilesGenerationDirectory, apiTemplate.TypedResources.ApiProducts);
1166+
var productTemplate = await this.GenerateProductsTemplateAsync(singleApiName, baseFilesGenerationDirectory);
11681167
var apiVersionSetTemplate = await this.GenerateApiVersionSetTemplateAsync(singleApiName, baseFilesGenerationDirectory, apiTemplate.TypedResources.Apis);
11691168
var authorizationServerTemplate = await this.GenerateAuthorizationServerTemplateAsync(singleApiName, baseFilesGenerationDirectory, apiTemplate.TypedResources.Apis);
11701169
var tagTemplate = await this.GenerateTagTemplateAsync(singleApiName, apiTemplate.TypedResources, productTemplate.TypedResources, baseFilesGenerationDirectory);

src/ArmTemplates/Extractor/EntityExtractors/APIExtractor.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,6 @@ public async Task<ApiTemplateResources> GetApiRelatedTemplateResourcesAsync(stri
206206
apiTemplateResources.ApiPolicies.Add(apiPolicyResource);
207207
}
208208

209-
var apiProducts = await this.productApisExtractor.GenerateSingleApiTemplateAsync(apiName, extractorParameters, addDependsOnParameter: true);
210-
if (!apiProducts.IsNullOrEmpty())
211-
{
212-
apiTemplateResources.ApiProducts = apiProducts;
213-
}
214-
215209
var apiTags = await this.tagExtractor.GenerateTagResourcesLinkedToApiAsync(apiName, extractorParameters);
216210
if (!apiTags.IsNullOrEmpty())
217211
{

src/ArmTemplates/Extractor/EntityExtractors/Abstractions/IProductApisExtractor.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,5 @@ Task<Template<ProductApiTemplateResources>> GenerateProductApisTemplateAsync(
1717
string singleApiName,
1818
List<string> multipleApiNames,
1919
ExtractorParameters extractorParameters);
20-
21-
Task<List<ProductApiTemplateResource>> GenerateSingleApiTemplateAsync(
22-
string singleApiName,
23-
ExtractorParameters extractorParameters,
24-
bool addDependsOnParameter = false);
2520
}
2621
}

src/ArmTemplates/Extractor/EntityExtractors/Abstractions/IProductExtractor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public interface IProductExtractor
1616
{
1717
Task<Template<ProductTemplateResources>> GenerateProductsTemplateAsync(
1818
string singleApiName,
19-
List<ProductApiTemplateResource> productApiTemplateResources,
2019
string baseFilesGenerationDirectory,
2120
ExtractorParameters extractorParameters);
2221
}

src/ArmTemplates/Extractor/EntityExtractors/MasterTemplateExtractor.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public Template<MasterTemplateResources> GenerateLinkedMasterTemplate(
7777
// api dependsOn
7878
var apiDependsOn = new List<string>();
7979
var productApiDependsOn = new List<string>();
80+
var productsDependsOn = new List<string>();
8081
var apiTagDependsOn = new List<string>();
8182
var globalPolicyDependsOn = new List<string>();
8283

@@ -86,6 +87,7 @@ public Template<MasterTemplateResources> GenerateLinkedMasterTemplate(
8687
const string NamedValuesTemplateName = "namedValuesTemplate";
8788

8889
dependsOnNamedValues = new string[] { $"[resourceId('{ResourceTypeConstants.ArmDeployments}', '{NamedValuesTemplateName}')]" };
90+
productsDependsOn.AddRange(dependsOnNamedValues);
8991
globalPolicyDependsOn.AddRange(dependsOnNamedValues);
9092
apiDependsOn.Add($"[resourceId('{ResourceTypeConstants.ArmDeployments}', '{NamedValuesTemplateName}')]");
9193
var namedValuesUri = this.GenerateLinkedTemplateUri(fileNames.NamedValues, extractorParameters);
@@ -139,16 +141,27 @@ public Template<MasterTemplateResources> GenerateLinkedMasterTemplate(
139141
masterResources.DeploymentResources.Add(apiVersionSetDeployment);
140142
}
141143

144+
if (groupTemplateResources?.HasContent() == true)
145+
{
146+
this.logger.LogDebug("Adding groups to master template");
147+
const string GroupsTemplate = "groupsTemplate";
148+
productsDependsOn.Add($"[resourceId('{ResourceTypeConstants.ArmDeployments}', '{GroupsTemplate}')]");
149+
150+
var groupsUri = this.GenerateLinkedTemplateUri(fileNames.Groups, extractorParameters);
151+
var groupsDeployment = CreateLinkedMasterTemplateResource(GroupsTemplate, groupsUri, Array.Empty<string>());
152+
153+
masterResources.DeploymentResources.Add(groupsDeployment);
154+
}
155+
142156
if (productsTemplateResources?.HasContent() == true)
143157
{
144158
this.logger.LogDebug("Adding products to master template");
145159
const string ProductsTemplate = "productsTemplate";
146160

147-
apiDependsOn.Add($"[resourceId('{ResourceTypeConstants.ArmDeployments}', '{ProductsTemplate}')]");
148161
productApiDependsOn.Add($"[resourceId('{ResourceTypeConstants.ArmDeployments}', '{ProductsTemplate}')]");
149162
var productsUri = this.GenerateLinkedTemplateUri(fileNames.Products, extractorParameters);
150163

151-
var productDeployment = CreateLinkedMasterTemplateResource(ProductsTemplate, productsUri, dependsOnNamedValues);
164+
var productDeployment = CreateLinkedMasterTemplateResource(ProductsTemplate, productsUri, productsDependsOn.ToArray());
152165

153166
if (extractorParameters.PolicyXMLBaseUrl is not null)
154167
{
@@ -269,17 +282,6 @@ public Template<MasterTemplateResources> GenerateLinkedMasterTemplate(
269282
masterResources.DeploymentResources.Add(apiTagsDeployment);
270283
}
271284

272-
if (groupTemplateResources?.HasContent() == true)
273-
{
274-
this.logger.LogDebug("Adding groups to master template");
275-
const string GroupsTemplate = "groupsTemplate";
276-
277-
var groupsUri = this.GenerateLinkedTemplateUri(fileNames.Groups, extractorParameters);
278-
var groupsDeployment = CreateLinkedMasterTemplateResource(GroupsTemplate, groupsUri, Array.Empty<string>());
279-
280-
masterResources.DeploymentResources.Add(groupsDeployment);
281-
}
282-
283285
if (identityProviderTemplateResources?.HasContent() == true)
284286
{
285287
this.logger.LogDebug("Adding identity providers to master template");

src/ArmTemplates/Extractor/EntityExtractors/ProductApisExtractor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ async Task<List<ProductApiTemplateResource>> GenerateProductApiTemplateResources
140140
}
141141

142142
productApiResources.Add(productApi);
143-
144143
}
145144
}
146145
catch (Exception ex)

src/ArmTemplates/Extractor/EntityExtractors/ProductExtractor.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ public ProductExtractor(
5252

5353
public async Task<Template<ProductTemplateResources>> GenerateProductsTemplateAsync(
5454
string singleApiName,
55-
List<ProductApiTemplateResource> productApiTemplateResources,
5655
string baseFilesGenerationDirectory,
5756
ExtractorParameters extractorParameters)
5857
{
@@ -61,15 +60,21 @@ public async Task<Template<ProductTemplateResources>> GenerateProductsTemplateAs
6160
.AddPolicyProperties(extractorParameters)
6261
.Build<ProductTemplateResources>();
6362

64-
var products = await this.productsClient.GetAllAsync(extractorParameters);
63+
var allProducts = await this.productsClient.GetAllAsync(extractorParameters);
6564

66-
foreach (var productTemplateResource in products)
65+
List<ProductApiTemplateResource> apiProducts = new List<ProductApiTemplateResource>();
66+
if (!singleApiName.IsNullOrEmpty())
67+
{
68+
apiProducts = await this.productsClient.GetAllLinkedToApiAsync(singleApiName, extractorParameters);
69+
}
70+
71+
foreach (var productTemplateResource in allProducts)
6772
{
6873
productTemplateResource.Name = $"[concat(parameters('{ParameterNames.ApimServiceName}'), '/{productTemplateResource.NewName}')]";
6974
productTemplateResource.ApiVersion = GlobalConstants.ApiVersion;
7075

7176
// only extract the product if this is a full extraction, or in the case of a single api, if it is found in products associated with the api
72-
if (singleApiName == null || productApiTemplateResources.Any(p => p.Name.Contains($"/{productTemplateResource.NewName}/")))
77+
if (singleApiName == null || apiProducts.Any(p => p.Name.Equals(productTemplateResource.NewName)))
7378
{
7479
this.logger.LogDebug("'{0}' product found", productTemplateResource.OriginalName);
7580
productsTemplate.TypedResources.Products.Add(productTemplateResource);

tests/ArmTemplates.Tests/Extractor/Scenarios/ApiExtractorByVersionSetNameTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public async Task GenerateAPIVersionSetTemplates_GeneratesApiTemplates()
111111

112112
var mockedProductExtractor = new Mock<IProductExtractor>(MockBehavior.Strict);
113113
mockedProductExtractor
114-
.Setup(x => x.GenerateProductsTemplateAsync(It.IsAny<string>(), It.IsAny<List<ProductApiTemplateResource>>(), It.IsAny<string>(), It.IsAny<ExtractorParameters>()))
114+
.Setup(x => x.GenerateProductsTemplateAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<ExtractorParameters>()))
115115
.ReturnsAsync(new Template<ProductTemplateResources>()
116116
{
117117
});

tests/ArmTemplates.Tests/Extractor/Scenarios/ApiExtractorTests.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public async Task GenerateApiTemplates_ProperlyLaysTheInformation()
112112
apiTemplate.Parameters.Should().ContainKey(ParameterNames.ApiLoggerId);
113113
apiTemplate.Parameters.Should().ContainKey(ParameterNames.PolicyXMLBaseUrl);
114114
apiTemplate.Parameters.Should().ContainKey(ParameterNames.PolicyXMLSasToken);
115-
apiTemplate.Resources.Count().Should().Be(33);
115+
apiTemplate.Resources.Count().Should().Be(30);
116116

117117
// apis
118118
apiTemplate.TypedResources.Apis.Count().Should().Be(3);
@@ -133,11 +133,6 @@ public async Task GenerateApiTemplates_ProperlyLaysTheInformation()
133133
apiTemplate.TypedResources.Tags.Count().Should().Be(6);
134134
apiTemplate.TypedResources.Tags.All(x => x.Type == ResourceTypeConstants.APITag).Should().BeTrue();
135135

136-
// api products
137-
apiTemplate.TypedResources.ApiProducts.Count().Should().Be(3);
138-
apiTemplate.TypedResources.ApiProducts.All(x => x.Type == ResourceTypeConstants.ProductApi).Should().BeTrue();
139-
apiTemplate.TypedResources.ApiProducts.All(x => x.Properties is not null).Should().BeTrue();
140-
141136
// api policies
142137
apiTemplate.TypedResources.ApiPolicies.Count().Should().Be(3);
143138
apiTemplate.TypedResources.ApiPolicies.All(x => x.Properties is not null).Should().BeTrue();

tests/ArmTemplates.Tests/Extractor/Scenarios/ProductApisExtractorTests.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Threading.Tasks;
1010
using FluentAssertions;
1111
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Commands.Executors;
12+
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.API.Utils;
1213
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Constants;
1314
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Templates.Builders;
1415
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Extractor.EntityExtractors;
@@ -77,5 +78,115 @@ public async Task GenerateProductApisTemplates_ProperlyLaysTheInformation()
7778
productApi.DependsOn.Should().BeNullOrEmpty();
7879
}
7980
}
81+
82+
[Fact]
83+
public async Task GenerateProductApisTemplates_GeneratesAllRelatedProductApis_GivenApiNameParameterProvided()
84+
{
85+
// arrange
86+
var apiName = "api-name";
87+
var currentTestDirectory = Path.Combine(this.OutputDirectory, nameof(GenerateProductApisTemplates_GeneratesAllRelatedProductApis_GivenApiNameParameterProvided));
88+
89+
var extractorConfig = this.GetDefaultExtractorConsoleAppConfiguration(apiName: apiName);
90+
var extractorParameters = new ExtractorParameters(extractorConfig);
91+
92+
var getSingleApiResponseFileLocation = Path.Combine(MockClientUtils.ApiClientJsonResponsesPath, "ApiManagementGetApiContract_success_response.json");
93+
var getRelatedProductsResponseFileLocation = Path.Combine(MockClientUtils.ApiClientJsonResponsesPath, "ApiManagementListApiProducts_success_response.json");
94+
var mockedApisClient = await MockApisClient.GetMockedHttpApiClient(
95+
new MockClientConfiguration(responseFileLocation: getSingleApiResponseFileLocation, urlPath: $"apis/{apiName}?api-version={GlobalConstants.ApiVersion}"));
96+
var mockedApiClientUtils = new ApiClientUtils(mockedApisClient, this.GetTestLogger<ApiClientUtils>());
97+
98+
var mockedServiceApisProductsApiClient = await MockProductsClient.GetMockedHttpProductClient(
99+
new MockClientConfiguration(responseFileLocation: getRelatedProductsResponseFileLocation)
100+
);
101+
102+
var productApisExtractor = new ProductApisExtractor(
103+
this.GetTestLogger<ProductApisExtractor>(),
104+
mockedServiceApisProductsApiClient,
105+
mockedApisClient,
106+
new TemplateBuilder());
107+
108+
var extractorExecutor = ExtractorExecutor.BuildExtractorExecutor(
109+
this.GetTestLogger<ExtractorExecutor>(),
110+
productApisExtractor: productApisExtractor);
111+
extractorExecutor.SetExtractorParameters(extractorParameters);
112+
113+
// act
114+
var productApisTemplate = await extractorExecutor.GenerateProductApisTemplateAsync(
115+
singleApiName: apiName,
116+
multipleApiNames: It.IsAny<List<string>>(),
117+
currentTestDirectory);
118+
119+
// assert
120+
File.Exists(Path.Combine(currentTestDirectory, extractorParameters.FileNames.ProductAPIs)).Should().BeTrue();
121+
122+
productApisTemplate.Parameters.Should().ContainKey(ParameterNames.ApimServiceName);
123+
productApisTemplate.TypedResources.ProductApis.Count().Should().Be(1);
124+
productApisTemplate.Resources.Count().Should().Be(1);
125+
126+
foreach (var productApi in productApisTemplate.TypedResources.ProductApis)
127+
{
128+
productApi.ApiVersion.Should().Be(GlobalConstants.ApiVersion);
129+
productApi.Name.Should().NotBeNullOrEmpty();
130+
productApi.Name.Contains($"/{apiName}").Should().BeTrue();
131+
productApi.Type.Should().Be(ResourceTypeConstants.ProductApi);
132+
productApi.Properties.DisplayName.Should().NotBeNullOrEmpty();
133+
productApi.Properties.Description.Should().NotBeNullOrEmpty();
134+
productApi.DependsOn.Should().BeNullOrEmpty();
135+
}
136+
}
137+
138+
[Fact]
139+
public async Task GenerateProductApisTemplates_GeneratesAllRelatedProductApis_GivenAllApisAreExtracted()
140+
{
141+
// arrange
142+
var currentTestDirectory = Path.Combine(this.OutputDirectory, nameof(GenerateProductApisTemplates_GeneratesAllRelatedProductApis_GivenAllApisAreExtracted));
143+
144+
var extractorConfig = this.GetDefaultExtractorConsoleAppConfiguration(apiName: string.Empty);
145+
var extractorParameters = new ExtractorParameters(extractorConfig);
146+
147+
var getAllApisResponseFileLocation = Path.Combine(MockClientUtils.ApiClientJsonResponsesPath, "ApiManagementListApis_success_response.json");
148+
var getRelatedProductsResponseFileLocation = Path.Combine(MockClientUtils.ApiClientJsonResponsesPath, "ApiManagementListApiProducts_success_response.json");
149+
var mockedApisClient = await MockApisClient.GetMockedHttpApiClient(
150+
new MockClientConfiguration(responseFileLocation: getAllApisResponseFileLocation));
151+
var mockedApiClientUtils = new ApiClientUtils(mockedApisClient, this.GetTestLogger<ApiClientUtils>());
152+
153+
var mockedServiceApisProductsApiClient = await MockProductsClient.GetMockedHttpProductClient(
154+
new MockClientConfiguration(responseFileLocation: getRelatedProductsResponseFileLocation)
155+
);
156+
157+
var productApisExtractor = new ProductApisExtractor(
158+
this.GetTestLogger<ProductApisExtractor>(),
159+
mockedServiceApisProductsApiClient,
160+
mockedApisClient,
161+
new TemplateBuilder());
162+
163+
var extractorExecutor = ExtractorExecutor.BuildExtractorExecutor(
164+
this.GetTestLogger<ExtractorExecutor>(),
165+
productApisExtractor: productApisExtractor);
166+
extractorExecutor.SetExtractorParameters(extractorParameters);
167+
168+
// act
169+
var productApisTemplate = await extractorExecutor.GenerateProductApisTemplateAsync(
170+
singleApiName: null,
171+
multipleApiNames: It.IsAny<List<string>>(),
172+
currentTestDirectory);
173+
174+
// assert
175+
File.Exists(Path.Combine(currentTestDirectory, extractorParameters.FileNames.ProductAPIs)).Should().BeTrue();
176+
177+
productApisTemplate.Parameters.Should().ContainKey(ParameterNames.ApimServiceName);
178+
productApisTemplate.TypedResources.ProductApis.Count().Should().Be(4);
179+
productApisTemplate.Resources.Count().Should().Be(4);
180+
181+
foreach (var productApi in productApisTemplate.TypedResources.ProductApis)
182+
{
183+
productApi.ApiVersion.Should().Be(GlobalConstants.ApiVersion);
184+
productApi.Name.Should().NotBeNullOrEmpty();
185+
productApi.Type.Should().Be(ResourceTypeConstants.ProductApi);
186+
productApi.Properties.DisplayName.Should().NotBeNullOrEmpty();
187+
productApi.Properties.Description.Should().NotBeNullOrEmpty();
188+
productApi.DependsOn.Should().BeNullOrEmpty();
189+
}
190+
}
80191
}
81192
}

0 commit comments

Comments
 (0)