3535
3636namespace 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