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

Commit 0bdf11b

Browse files
f-alizadaFarhad Alizada
andauthored
Refactor/tag creator (#732)
* Refactor TagTemplateCreator and tests. * Resource name generator helper added * Introduce error messages and exception for duplicate sanitized tag's display name * Update documentation for tags creation * Combine common utilities into NamingHelper * Change path generation for templates in the CreatorExecutor. Add CreatorExecutorTests Co-authored-by: Farhad Alizada <falizada@microsoft.com>
1 parent 1bd13d4 commit 0bdf11b

25 files changed

+460
-77
lines changed

src/ArmTemplates/Commands/Executors/CreatorExecutor.cs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Linq;
99
using System.Threading.Tasks;
1010
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Constants;
11+
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Extensions;
1112
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.FileHandlers;
1213
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Templates.Abstractions;
1314
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Creator.Models.Parameters;
@@ -203,9 +204,7 @@ public async Task ExecuteGenerationBasedOnConfiguration()
203204
}
204205
}
205206

206-
this.logger.LogInformation("Creating tag template");
207-
this.logger.LogInformation("------------------------------------------");
208-
var tagTemplate = this.creatorParameters.Tags != null ? this.tagTemplateCreator.CreateTagTemplate(this.creatorParameters) : null;
207+
var tagTemplate = await this.GenerateTagsTemplateAsync();
209208

210209
// create parameters file
211210
var templateParameters = this.masterTemplateCreator.CreateMasterTemplateParameterValues(this.creatorParameters);
@@ -242,44 +241,59 @@ public async Task ExecuteGenerationBasedOnConfiguration()
242241
}
243242
if (globalServicePolicyTemplate != null)
244243
{
245-
FileWriter.WriteJSONToFile(globalServicePolicyTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.GlobalServicePolicy));
244+
FileWriter.WriteJSONToFile(globalServicePolicyTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.GlobalServicePolicy));
246245
}
247246
if (apiVersionSetsTemplate != null)
248247
{
249-
FileWriter.WriteJSONToFile(apiVersionSetsTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.ApiVersionSets));
248+
FileWriter.WriteJSONToFile(apiVersionSetsTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.ApiVersionSets));
250249
}
251250
if (productsTemplate != null)
252251
{
253-
FileWriter.WriteJSONToFile(productsTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Products));
252+
FileWriter.WriteJSONToFile(productsTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Products));
254253
}
255254
if (productAPIsTemplate != null)
256255
{
257-
FileWriter.WriteJSONToFile(productAPIsTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.ProductAPIs));
256+
FileWriter.WriteJSONToFile(productAPIsTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.ProductAPIs));
258257
}
259258
if (propertyTemplate != null)
260259
{
261-
FileWriter.WriteJSONToFile(propertyTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.NamedValues));
260+
FileWriter.WriteJSONToFile(propertyTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.NamedValues));
262261
}
263262
if (loggersTemplate != null)
264263
{
265-
FileWriter.WriteJSONToFile(loggersTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Loggers));
264+
FileWriter.WriteJSONToFile(loggersTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Loggers));
266265
}
267266
if (backendsTemplate != null)
268267
{
269-
FileWriter.WriteJSONToFile(backendsTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Backends));
268+
FileWriter.WriteJSONToFile(backendsTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Backends));
270269
}
271270
if (authorizationServersTemplate != null)
272271
{
273-
FileWriter.WriteJSONToFile(authorizationServersTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.AuthorizationServers));
272+
FileWriter.WriteJSONToFile(authorizationServersTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.AuthorizationServers));
274273
}
275274
if (tagTemplate != null)
276275
{
277-
FileWriter.WriteJSONToFile(tagTemplate, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Tags));
276+
FileWriter.WriteJSONToFile(tagTemplate, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Tags));
278277
}
279278

280279
// write parameters to outputLocation
281-
FileWriter.WriteJSONToFile(templateParameters, string.Concat(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Parameters));
280+
FileWriter.WriteJSONToFile(templateParameters, Path.Combine(this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Parameters));
282281
this.logger.LogInformation("Templates written to output location");
283282
}
283+
284+
public async Task<Template> GenerateTagsTemplateAsync()
285+
{
286+
if (this.creatorParameters.Tags.IsNullOrEmpty() && this.creatorParameters.Apis.All(x => x.Tags.IsNullOrEmpty()))
287+
{
288+
return null;
289+
}
290+
291+
this.logger.LogInformation("Creating tag template");
292+
293+
var tagTemplate = this.tagTemplateCreator.CreateTagTemplate(this.creatorParameters);
294+
await FileWriter.SaveAsJsonAsync(tagTemplate, this.creatorParameters.OutputLocation, this.creatorParameters.FileNames.Tags);
295+
296+
return tagTemplate;
297+
}
284298
}
285299
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// --------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License.
4+
// --------------------------------------------------------------------------
5+
6+
namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Constants
7+
{
8+
public static class ErrorMessages
9+
{
10+
public const string DuplicateTagResourceNameErrorMessage = "Duplicate tag resource name found during sanitizing the display name. Please consider renaming tags: {0}, {1}. Both resulted resource name to be equal to: {2}";
11+
public const string EmptyResourceNameAfterSanitizingErrorMessage = "Sanitizing the display name '{0}' resulted empty string";
12+
}
13+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// --------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License.
4+
// --------------------------------------------------------------------------
5+
6+
using System;
7+
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Constants;
8+
9+
namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Exceptions
10+
{
11+
public class DuplicateTagResourceNameException : Exception
12+
{
13+
public DuplicateTagResourceNameException(string existingValue, string tagName, string resourceName): base (string.Format(ErrorMessages.DuplicateTagResourceNameErrorMessage, existingValue, tagName, resourceName))
14+
{
15+
}
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// --------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License.
4+
// --------------------------------------------------------------------------
5+
6+
using System;
7+
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Constants;
8+
9+
namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Exceptions
10+
{
11+
public class EmptyResourceNameException : Exception
12+
{
13+
public EmptyResourceNameException(string resourceName) : base(string.Format(ErrorMessages.EmptyResourceNameAfterSanitizingErrorMessage, resourceName))
14+
{
15+
}
16+
}
17+
}

src/ArmTemplates/Common/Extensions/CollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Extensions
1010
{
11-
static class CollectionExtensions
11+
public static class CollectionExtensions
1212
{
1313
public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection)
1414
{

src/ArmTemplates/Common/Extensions/ParameterNamingHelper.cs renamed to src/ArmTemplates/Common/Extensions/NamingHelper.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88

99
namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Extensions
1010
{
11-
static class ParameterNamingHelper
11+
public static class NamingHelper
1212
{
1313
static readonly Regex ExcludeOtherFromLettersAndDigitsRegex = new Regex("[^a-zA-Z0-9]");
14+
static readonly Regex ExcludeOtherFromAlphaNumericsAndHyphensRegex = new Regex("[^a-zA-Z0-9-]");
1415

1516
public static string GetSubstringBetweenTwoCharacters(char left, char right, string fullString)
1617
{
@@ -42,5 +43,22 @@ public static string GenerateValidParameterName(string apiName, string prefix)
4243
return validApiName;
4344
}
4445
}
46+
47+
public static string GenerateValidResourceNameFromDisplayName(string displayName)
48+
{
49+
if (string.IsNullOrEmpty(displayName))
50+
{
51+
return string.Empty;
52+
}
53+
54+
var trimmedDisplayName = displayName.Trim().Replace(" ", "-");
55+
var resourceName = ExcludeOtherFromAlphaNumericsAndHyphensRegex.Replace(trimmedDisplayName, string.Empty);
56+
return resourceName;
57+
}
58+
59+
public static string GenerateParametrizedResourceName(string parameterName, string resourceName)
60+
{
61+
return $"[concat(parameters('{parameterName}'), '/{resourceName}')]";
62+
}
4563
}
4664
}

src/ArmTemplates/Common/Templates/Logger/LoggerTemplateResources.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public void SetLoggerResourceIdForEachLogger()
5151
static string GetValidLoggerParamName(string resourceName)
5252
{
5353
var loggerNameStrs = resourceName.Split(new char[] { ',' });
54-
var validLoggerName = ParameterNamingHelper.GenerateValidParameterName(loggerNameStrs[loggerNameStrs.Length - 1], ParameterPrefix.LogResourceId);
54+
var validLoggerName = NamingHelper.GenerateValidParameterName(loggerNameStrs[loggerNameStrs.Length - 1], ParameterPrefix.LogResourceId);
5555
return validLoggerName;
5656
}
5757
}

src/ArmTemplates/Creator/Models/Parameters/CreatorParameters.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ internal void OverrideParameters(CreateConsoleAppConfiguration configuration, Fi
7474
}
7575
}
7676

77-
internal void GenerateFileNames()
77+
public void GenerateFileNames()
7878
{
7979
this.FileNames = this.BaseFileName == null
8080
? FileNameGenerator.GenerateFileNames(this.ApimServiceName)

src/ArmTemplates/Creator/TemplateCreators/Abstractions/ITagTemplateCreator.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@
33
// Licensed under the MIT License.
44
// --------------------------------------------------------------------------
55

6-
using System;
7-
using System.Collections.Generic;
8-
using System.Linq;
9-
using System.Text;
10-
using System.Threading.Tasks;
116
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Templates.Abstractions;
127
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Creator.Models.Parameters;
138

src/ArmTemplates/Creator/TemplateCreators/PropertyTemplateCreator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ public Template CreatePropertyTemplate(CreatorParameters creatorConfig)
5353
{
5454
string value = namedValue.Value == null ? null
5555
: creatorConfig.ParameterizeNamedValues
56-
? $"[parameters('{ParameterNames.NamedValues}').{ParameterNamingHelper.GenerateValidParameterName(namedValue.DisplayName, ParameterPrefix.Property)}]"
56+
? $"[parameters('{ParameterNames.NamedValues}').{NamingHelper.GenerateValidParameterName(namedValue.DisplayName, ParameterPrefix.Property)}]"
5757
: namedValue.Value;
5858

5959
var keyVault = namedValue.KeyVault == null ? null
6060
: creatorConfig.ParameterizeNamedValues
6161
? new NamedValueResourceKeyVaultProperties
6262
{
63-
SecretIdentifier = $"[parameters('{ParameterNames.NamedValueKeyVaultSecrets}').{ParameterNamingHelper.GenerateValidParameterName(namedValue.DisplayName, ParameterPrefix.Property)}]"
63+
SecretIdentifier = $"[parameters('{ParameterNames.NamedValueKeyVaultSecrets}').{NamingHelper.GenerateValidParameterName(namedValue.DisplayName, ParameterPrefix.Property)}]"
6464
}
6565
: namedValue.KeyVault;
6666

src/ArmTemplates/Creator/TemplateCreators/TagTemplateCreator.cs

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Templates.Builders.Abstractions;
1111
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Creator.Models.Parameters;
1212
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Creator.TemplateCreators.Abstractions;
13+
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Extensions;
14+
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common.Exceptions;
1315

1416
namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Creator.TemplateCreators
1517
{
@@ -22,53 +24,71 @@ public TagTemplateCreator(ITemplateBuilder templateBuilder)
2224
this.templateBuilder = templateBuilder;
2325
}
2426

25-
public Template CreateTagTemplate(CreatorParameters creatorConfig)
27+
static void AddTagNameToDictionary(string tagName, Dictionary<string, string> tagsDictionary)
2628
{
27-
// create empty template
28-
Template tagTemplate = this.templateBuilder.GenerateEmptyTemplate().Build();
29+
var resourceName = NamingHelper.GenerateValidResourceNameFromDisplayName(tagName);
2930

30-
// add parameters
31-
tagTemplate.Parameters = new Dictionary<string, TemplateParameterProperties>
31+
if (string.IsNullOrEmpty(resourceName))
3232
{
33-
{ParameterNames.ApimServiceName, new TemplateParameterProperties(){ Type = "string" }}
34-
};
33+
throw new EmptyResourceNameException(tagName);
34+
}
35+
36+
if (tagsDictionary.ContainsKey(resourceName))
37+
{
38+
var existingValue = tagsDictionary[resourceName];
39+
if (!existingValue.Equals(tagName))
40+
{
41+
throw new DuplicateTagResourceNameException(existingValue, tagName, resourceName);
42+
}
43+
}
44+
else
45+
{
46+
tagsDictionary.Add(resourceName, tagName);
47+
}
48+
}
3549

36-
// aggregate all tags from apis
37-
HashSet<string> tagHashset = new HashSet<string>();
38-
List<ApiConfig> apis = creatorConfig.Apis;
39-
if (apis != null)
50+
public Template CreateTagTemplate(CreatorParameters creatorConfig)
51+
{
52+
var tagTemplate = this.templateBuilder.GenerateTemplateWithApimServiceNameProperty().Build();
53+
var tagsDictionary = new Dictionary<string, string>();
54+
55+
if (!creatorConfig.Apis.IsNullOrEmpty())
4056
{
41-
foreach (ApiConfig api in apis)
57+
foreach (var api in creatorConfig.Apis)
4258
{
43-
if (api.Tags != null)
59+
if (!api.Tags.IsNullOrEmpty())
4460
{
45-
string[] apiTags = api.Tags.Split(", ");
46-
foreach (string apiTag in apiTags)
61+
var apiTags = api.Tags.Split(",");
62+
63+
foreach (var apiTag in apiTags)
4764
{
48-
tagHashset.Add(apiTag);
65+
AddTagNameToDictionary(apiTag, tagsDictionary);
4966
}
5067
}
5168
}
5269
}
53-
foreach (TagProperties tag in creatorConfig.Tags)
70+
71+
if (!creatorConfig.Tags.IsNullOrEmpty())
5472
{
55-
tagHashset.Add(tag.DisplayName);
73+
foreach (var tag in creatorConfig.Tags)
74+
{
75+
AddTagNameToDictionary(tag.DisplayName, tagsDictionary);
76+
}
5677
}
5778

58-
List<TemplateResource> resources = new List<TemplateResource>();
59-
foreach (string tag in tagHashset)
79+
var resources = new List<TemplateResource>();
80+
81+
foreach (var (tagResourceName, tagDisplayName) in tagsDictionary)
6082
{
61-
// create tag resource with properties
62-
TagTemplateResource tagTemplateResource = new TagTemplateResource()
83+
var tagTemplateResource = new TagTemplateResource()
6384
{
64-
Name = $"[concat(parameters('{ParameterNames.ApimServiceName}'), '/{tag}')]",
85+
Name = NamingHelper.GenerateParametrizedResourceName(ParameterNames.ApimServiceName, tagResourceName),
6586
Type = ResourceTypeConstants.Tag,
6687
ApiVersion = GlobalConstants.ApiVersion,
6788
Properties = new TagProperties()
6889
{
69-
DisplayName = tag
70-
},
71-
DependsOn = new string[] { }
90+
DisplayName = tagDisplayName
91+
}
7292
};
7393
resources.Add(tagTemplateResource);
7494
}

src/ArmTemplates/Extractor/EntityExtractors/APIExtractor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ void SetArmTemplateValuesToApiTemplateResource(ApiTemplateResource apiResource,
142142

143143
if (extractorParameters.ParameterizeServiceUrl)
144144
{
145-
apiResource.Properties.ServiceUrl = $"[parameters('{ParameterNames.ServiceUrl}').{ParameterNamingHelper.GenerateValidParameterName(originalServiceApiName, ParameterPrefix.Api)}]";
145+
apiResource.Properties.ServiceUrl = $"[parameters('{ParameterNames.ServiceUrl}').{NamingHelper.GenerateValidParameterName(originalServiceApiName, ParameterPrefix.Api)}]";
146146
}
147147

148148
if (extractorParameters.ParametrizeApiOauth2Scope)
149149
{
150150
if (apiResource.Properties.AuthenticationSettings?.OAuth2?.Scope is not null)
151151
{
152-
apiResource.Properties.AuthenticationSettings.OAuth2.Scope = $"[parameters('{ParameterNames.ApiOauth2ScopeSettings}').{ParameterNamingHelper.GenerateValidParameterName(originalServiceApiName, ParameterPrefix.ApiOauth2Scope)}]";
152+
apiResource.Properties.AuthenticationSettings.OAuth2.Scope = $"[parameters('{ParameterNames.ApiOauth2ScopeSettings}').{NamingHelper.GenerateValidParameterName(originalServiceApiName, ParameterPrefix.ApiOauth2Scope)}]";
153153
}
154154
}
155155

src/ArmTemplates/Extractor/EntityExtractors/ApiRevisionExtractor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ async Task<ApiTemplateResource> GenerateApiTemplateResourceAsVersioned(string ap
118118

119119
if (extractorParameters.ParameterizeServiceUrl)
120120
{
121-
apiDetails.Properties.ServiceUrl = $"[parameters('{ParameterNames.ServiceUrl}').{ParameterNamingHelper.GenerateValidParameterName(apiName, ParameterPrefix.Api)}]";
121+
apiDetails.Properties.ServiceUrl = $"[parameters('{ParameterNames.ServiceUrl}').{NamingHelper.GenerateValidParameterName(apiName, ParameterPrefix.Api)}]";
122122
}
123123

124124
if (apiDetails.Properties.ApiVersionSetId != null)

src/ArmTemplates/Extractor/EntityExtractors/BackendExtractor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ void SaveBackendApiParametersToCache()
110110
}
111111

112112
var backendApiParameters = new BackendApiParameters();
113-
var backendValidName = ParameterNamingHelper.GenerateValidParameterName(originalBackendName, ParameterPrefix.Backend).ToLower();
113+
var backendValidName = NamingHelper.GenerateValidParameterName(originalBackendName, ParameterPrefix.Backend).ToLower();
114114

115115
if (!string.IsNullOrEmpty(backendResource.Properties.ResourceId))
116116
{

0 commit comments

Comments
 (0)