Skip to content

Commit 739af20

Browse files
authored
Update check rules for UX metadata (#20582)
1 parent 966a48c commit 739af20

File tree

5 files changed

+96
-36
lines changed

5 files changed

+96
-36
lines changed

src/Aks/Aks/UX/Microsoft.ContainerService/managedClusters-agentPools.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@
2626
},
2727
"examples": [
2828
{
29-
"description": "Get the agentpool in the managed cluster and resource group..",
29+
"description": "Get the agentpool in the managed cluster and resource group.",
3030
"parameters": [
3131
{
3232
"name": "-ResourceGroupName",
3333
"value": "[path.resourceGroupName]"
3434
},
3535
{
3636
"name": "-ClusterName",
37-
"value": "[path.clusterName]"
37+
"value": "[path.resourceName]"
3838
},
3939
{
4040
"name": "-Name",

src/Sql/Sql/UX/Microsoft.Sql/sqlDatabase.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{
99
"name":"Get-AzSqlDatabase",
1010
"description":"Gets one or more databases.",
11-
"path":"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Sql/servers/{ServerName}/databases",
11+
"path":"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Sql/servers/{ServerName}/databases/{databaseName}",
1212
"confirmation":false,
1313
"help":{
1414
"learnMore":{
@@ -41,6 +41,10 @@
4141
{
4242
"name":"-ServerName",
4343
"value":"[path.ServerName]"
44+
},
45+
{
46+
"name":"-DatabaseName",
47+
"value":"[path.DatabaseName]"
4448
}
4549
]
4650
}
@@ -49,7 +53,7 @@
4953
{
5054
"name":"Remove-AzSqlDatabase",
5155
"description":"Removes an Azure SQL database.",
52-
"path":"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Sql/servers/{ServerName}/databases",
56+
"path":"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Sql/servers/{ServerName}/databases/{databaseName}",
5357
"confirmation":false,
5458
"help":{
5559
"learnMore":{
@@ -88,7 +92,7 @@
8892
{
8993
"name":"Suspend-AzSqlDatabase",
9094
"description":"Suspends a SQL Data Warehouse database.",
91-
"path":"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Sql/servers/{ServerName}/databases",
95+
"path":"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Sql/servers/{ServerName}/databases/{databaseName}",
9296
"confirmation":false,
9397
"help":{
9498
"learnMore":{

tools/StaticAnalysis/UXMetadataAnalyzer/UXMetadataAnalyzer.cs

Lines changed: 87 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
3535

3636
namespace StaticAnalysis.UXMetadataAnalyzer
3737
{
38+
public class IssueLoggerContext
39+
{
40+
public string ModuleName { get; set; }
41+
public string ResourceType { get; set; }
42+
public string SubResourceType { get; set; }
43+
public string CommandName { get; set; }
44+
}
45+
3846
public class UXMetadataAnalyzer : IStaticAnalyzer
3947
{
4048
public AnalysisLogger Logger { get; set; }
@@ -89,7 +97,7 @@ public void Analyze(
8997
!ModuleFilter.IsAzureStackModule(s) && Directory.Exists(Path.GetFullPath(s))))
9098
{
9199
SharedAssemblyLoader.Load(baseDirectory);
92-
var probingDirectories = new List<string> {baseDirectory};
100+
var probingDirectories = new List<string> { baseDirectory };
93101

94102
// Add current directory for probing
95103
probingDirectories.AddRange(Directory.EnumerateDirectories(Path.GetFullPath(baseDirectory)));
@@ -124,54 +132,53 @@ public void Analyze(
124132
}
125133
}
126134

127-
private void ValidateSchema(string moduleName, string resourceType, string subResourceType, string UXMetadataContent, ReportLogger<UXMetadataIssue> issueLogger)
135+
private void ValidateSchema(IssueLoggerContext context, string UXMetadataContent, ReportLogger<UXMetadataIssue> issueLogger)
128136
{
129137
var result = schemaValidator.Validate(UXMetadataContent, schema);
130138
if (result != null && result.Count != 0)
131139
{
132140
foreach (ValidationError error in result)
133141
{
134-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, null, 1, error.ToString().Replace("\n", "\\n"));
142+
issueLogger.LogUXMetadataIssue(context, 1, error.ToString().Replace("\n", "\\n"));
135143
}
136144
}
137145
}
138146

139-
private void ValidateMetadata(string moduleName, string resourceType, string subResourceType, string UXMetadataContent, ModuleMetadata moduleMetadata, ReportLogger<UXMetadataIssue> issueLogger)
147+
private void ValidateMetadata(IssueLoggerContext context, string UXMetadataContent, ModuleMetadata moduleMetadata, ReportLogger<UXMetadataIssue> issueLogger)
140148
{
141149
UXMetadata UXMetadata = JsonConvert.DeserializeObject<UXMetadata>(UXMetadataContent);
142-
150+
143151
foreach (UXMetadataCommand command in UXMetadata.Commands)
144152
{
145-
string expectLearnUrl = string.Format("https://learn.microsoft.com/powershell/module/{0}/{1}", moduleName, command.Name).ToLower();
146-
153+
context.CommandName = command.Name;
154+
string expectLearnUrl = string.Format("https://learn.microsoft.com/powershell/module/{0}/{1}", context.ModuleName, command.Name).ToLower();
155+
147156
if (!expectLearnUrl.Equals(command.Help.LearnMore.Url, StringComparison.OrdinalIgnoreCase))
148157
{
149158
string description = string.Format("Doc url is expect: {0} but get: {1}", expectLearnUrl, command.Help.LearnMore.Url);
150-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, command.Name, 1, description);
159+
issueLogger.LogUXMetadataIssue(context, 1, description);
151160
}
152-
if (command.Path.IndexOf(resourceType, StringComparison.CurrentCultureIgnoreCase) == -1)
161+
if (command.Path.IndexOf(context.ResourceType, StringComparison.CurrentCultureIgnoreCase) == -1)
153162
{
154-
string description = string.Format("The path {0} doesn't contains the right resource tpye: {1}", command.Path, resourceType);
155-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, command.Name, 2, description);
163+
string description = string.Format("The path {0} doesn't contains the right resource tpye: {1}", command.Path, context.ResourceType);
164+
issueLogger.LogUXMetadataIssue(context, 2, description);
156165
}
157166

158167
CmdletMetadata cmdletMetadata = moduleMetadata.Cmdlets.Find(x => x.Name == command.Name);
159168
if (cmdletMetadata == null)
160169
{
161-
string description = string.Format("Cmdlet {0} is not contained in {1}.", command.Name, moduleName);
162-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, command.Name, 1, description);
170+
string description = string.Format("Cmdlet {0} is not contained in {1}.", command.Name, context.ModuleName);
171+
issueLogger.LogUXMetadataIssue(context, 1, description);
172+
continue;
163173
}
164-
else
174+
foreach (UXMetadataCommandExample example in command.Examples)
165175
{
166-
foreach (UXMetadataCommandExample example in command.Examples)
167-
{
168-
ValidateExample(moduleName, resourceType, subResourceType, command.Name, cmdletMetadata, example, issueLogger);
169-
}
176+
ValidateExample(context, command, cmdletMetadata, example, issueLogger);
170177
}
171178
}
172179
}
173180

174-
private void ValidateExample(string moduleName, string resourceType, string subResourceType, string commandName, CmdletMetadata cmdletMetadata, UXMetadataCommandExample example, ReportLogger<UXMetadataIssue> issueLogger)
181+
private void ValidateExample(IssueLoggerContext context, UXMetadataCommand command, CmdletMetadata cmdletMetadata, UXMetadataCommandExample example, ReportLogger<UXMetadataIssue> issueLogger)
175182
{
176183
List<string> parameterListConvertedFromAlias = example.Parameters.Select(x =>
177184
{
@@ -187,13 +194,13 @@ private void ValidateExample(string moduleName, string resourceType, string subR
187194
if (alias.Equals(parameterNameInExample, StringComparison.CurrentCultureIgnoreCase))
188195
{
189196
string issueDescription = string.Format("Please use parameter {0} instead of alias {1}", parameterMetadata.Name, alias);
190-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, commandName, 2, issueDescription);
197+
issueLogger.LogUXMetadataIssue(context, 2, issueDescription);
191198
return parameterMetadata.Name;
192199
}
193200
}
194201
}
195202
string description = string.Format("Cannot find the defination of parameter {0} in example", parameterNameInExample);
196-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, commandName, 1, description);
203+
issueLogger.LogUXMetadataIssue(context, 1, description);
197204
return null;
198205
}).ToList();
199206

@@ -203,7 +210,7 @@ private void ValidateExample(string moduleName, string resourceType, string subR
203210
if (parameterListConvertedFromAlias.Count(x => parameter.Equals(x)) != 1)
204211
{
205212
string description = string.Format("Multiply reference of parameter {0} in example", parameter);
206-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, commandName, 1, description);
213+
issueLogger.LogUXMetadataIssue(context, 1, description);
207214
}
208215
}
209216
if (parameterListConvertedFromAlias.Contains(null))
@@ -222,8 +229,51 @@ private void ValidateExample(string moduleName, string resourceType, string subR
222229

223230
if (!findMatchedParameterSet)
224231
{
225-
string description = string.Format("Cannot find a matched parameter set for example of {0}", commandName);
226-
issueLogger.LogUXMetadataIssue(moduleName, resourceType, subResourceType, commandName, 1, description);
232+
string description = string.Format("Cannot find a matched parameter set for example of {0}", context.CommandName);
233+
issueLogger.LogUXMetadataIssue(context, 1, description);
234+
}
235+
236+
#region valiate the parameters in path
237+
var httpPathParameterRegex = new Regex(@"\{\w+\}");
238+
HashSet<string> parametersFromHttpPath = new HashSet<string>(httpPathParameterRegex.Matches(command.Path).Select(x => x.Value.TrimStart('{').TrimEnd('}')), StringComparer.OrdinalIgnoreCase);
239+
ValidateParametersDefinedInPathContainsInExample(context, parametersFromHttpPath, example, issueLogger);
240+
ValidateParametersInExampleDefinedInPath(context, parametersFromHttpPath, example, issueLogger);
241+
#endregion
242+
}
243+
244+
private void ValidateParametersDefinedInPathContainsInExample(IssueLoggerContext context, HashSet<string> parametersFromHttpPath, UXMetadataCommandExample example, ReportLogger<UXMetadataIssue> issueLogger)
245+
{
246+
foreach (string parameterFromHttpPath in parametersFromHttpPath)
247+
{
248+
if (parameterFromHttpPath.Equals("subscriptionId", StringComparison.CurrentCultureIgnoreCase))
249+
{
250+
continue;
251+
}
252+
bool isParameterContainsInExample = example.Parameters.Any(x => x.Value.Equals(string.Format("[path.{0}]", parameterFromHttpPath), StringComparison.CurrentCultureIgnoreCase));
253+
if (!isParameterContainsInExample)
254+
{
255+
string description = string.Format("{0} is defined in path but cannot find in example", parameterFromHttpPath);
256+
issueLogger.LogUXMetadataIssue(context, 1, description);
257+
}
258+
}
259+
}
260+
261+
private void ValidateParametersInExampleDefinedInPath(IssueLoggerContext context, HashSet<string> parametersFromHttpPath, UXMetadataCommandExample example, ReportLogger<UXMetadataIssue> issueLogger)
262+
{
263+
var exampleParameterPathRegex = new Regex(@"path\.([\w]+)");
264+
foreach (string parameterInExample in example.Parameters.Select(x => x.Value))
265+
{
266+
var match = exampleParameterPathRegex.Match(parameterInExample);
267+
if (!match.Success)
268+
{
269+
continue;
270+
}
271+
var parameterName = match.Groups[1].Value;
272+
if (!parametersFromHttpPath.Contains(parameterName))
273+
{
274+
string description = string.Format("{0} is defined in example but cannot find in http path", parameterName);
275+
issueLogger.LogUXMetadataIssue(context, 1, description);
276+
}
227277
}
228278
}
229279

@@ -267,8 +317,14 @@ private void ValidateUXMetadata(string moduleName, string UXMetadataPath, Module
267317
string UXMetadataContent = File.ReadAllText(UXMetadataPath);
268318
string resourceType = Path.GetFileName(Path.GetDirectoryName(UXMetadataPath));
269319
string subResourceType = Path.GetFileName(UXMetadataPath).Replace(".json", "");
270-
ValidateSchema(moduleName, resourceType, subResourceType, UXMetadataContent, issueLogger);
271-
ValidateMetadata(moduleName, resourceType, subResourceType, UXMetadataContent, moduleMetadata, issueLogger);
320+
IssueLoggerContext context = new IssueLoggerContext
321+
{
322+
ModuleName = moduleName,
323+
ResourceType = resourceType,
324+
SubResourceType = subResourceType
325+
};
326+
ValidateSchema(context, UXMetadataContent, issueLogger);
327+
ValidateMetadata(context, UXMetadataContent, moduleMetadata, issueLogger);
272328
}
273329

274330

@@ -290,15 +346,15 @@ public AnalysisReport GetAnalysisReport()
290346
public static class LogExtensions
291347
{
292348
public static void LogUXMetadataIssue(
293-
this ReportLogger<UXMetadataIssue> issueLogger, string module, string resourceType, string subResourceType, string command,
349+
this ReportLogger<UXMetadataIssue> issueLogger, IssueLoggerContext context,
294350
int severity, string description)
295351
{
296352
issueLogger.LogRecord(new UXMetadataIssue
297353
{
298-
Module = module,
299-
ResourceType = resourceType,
300-
SubResourceType = subResourceType,
301-
Command = command,
354+
Module = context.ModuleName,
355+
ResourceType = context.ResourceType,
356+
SubResourceType = context.SubResourceType,
357+
Command = context.CommandName,
302358
Description = description,
303359
Severity = severity,
304360
});

0 commit comments

Comments
 (0)