35
35
36
36
namespace StaticAnalysis . UXMetadataAnalyzer
37
37
{
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
+
38
46
public class UXMetadataAnalyzer : IStaticAnalyzer
39
47
{
40
48
public AnalysisLogger Logger { get ; set ; }
@@ -89,7 +97,7 @@ public void Analyze(
89
97
! ModuleFilter . IsAzureStackModule ( s ) && Directory . Exists ( Path . GetFullPath ( s ) ) ) )
90
98
{
91
99
SharedAssemblyLoader . Load ( baseDirectory ) ;
92
- var probingDirectories = new List < string > { baseDirectory } ;
100
+ var probingDirectories = new List < string > { baseDirectory } ;
93
101
94
102
// Add current directory for probing
95
103
probingDirectories . AddRange ( Directory . EnumerateDirectories ( Path . GetFullPath ( baseDirectory ) ) ) ;
@@ -124,54 +132,53 @@ public void Analyze(
124
132
}
125
133
}
126
134
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 )
128
136
{
129
137
var result = schemaValidator . Validate ( UXMetadataContent , schema ) ;
130
138
if ( result != null && result . Count != 0 )
131
139
{
132
140
foreach ( ValidationError error in result )
133
141
{
134
- issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , null , 1 , error . ToString ( ) . Replace ( "\n " , "\\ n" ) ) ;
142
+ issueLogger . LogUXMetadataIssue ( context , 1 , error . ToString ( ) . Replace ( "\n " , "\\ n" ) ) ;
135
143
}
136
144
}
137
145
}
138
146
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 )
140
148
{
141
149
UXMetadata UXMetadata = JsonConvert . DeserializeObject < UXMetadata > ( UXMetadataContent ) ;
142
-
150
+
143
151
foreach ( UXMetadataCommand command in UXMetadata . Commands )
144
152
{
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
+
147
156
if ( ! expectLearnUrl . Equals ( command . Help . LearnMore . Url , StringComparison . OrdinalIgnoreCase ) )
148
157
{
149
158
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 ) ;
151
160
}
152
- if ( command . Path . IndexOf ( resourceType , StringComparison . CurrentCultureIgnoreCase ) == - 1 )
161
+ if ( command . Path . IndexOf ( context . ResourceType , StringComparison . CurrentCultureIgnoreCase ) == - 1 )
153
162
{
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 ) ;
156
165
}
157
166
158
167
CmdletMetadata cmdletMetadata = moduleMetadata . Cmdlets . Find ( x => x . Name == command . Name ) ;
159
168
if ( cmdletMetadata == null )
160
169
{
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 ;
163
173
}
164
- else
174
+ foreach ( UXMetadataCommandExample example in command . Examples )
165
175
{
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 ) ;
170
177
}
171
178
}
172
179
}
173
180
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 )
175
182
{
176
183
List < string > parameterListConvertedFromAlias = example . Parameters . Select ( x =>
177
184
{
@@ -187,13 +194,13 @@ private void ValidateExample(string moduleName, string resourceType, string subR
187
194
if ( alias . Equals ( parameterNameInExample , StringComparison . CurrentCultureIgnoreCase ) )
188
195
{
189
196
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 ) ;
191
198
return parameterMetadata . Name ;
192
199
}
193
200
}
194
201
}
195
202
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 ) ;
197
204
return null ;
198
205
} ) . ToList ( ) ;
199
206
@@ -203,7 +210,7 @@ private void ValidateExample(string moduleName, string resourceType, string subR
203
210
if ( parameterListConvertedFromAlias . Count ( x => parameter . Equals ( x ) ) != 1 )
204
211
{
205
212
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 ) ;
207
214
}
208
215
}
209
216
if ( parameterListConvertedFromAlias . Contains ( null ) )
@@ -222,8 +229,51 @@ private void ValidateExample(string moduleName, string resourceType, string subR
222
229
223
230
if ( ! findMatchedParameterSet )
224
231
{
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
+ }
227
277
}
228
278
}
229
279
@@ -267,8 +317,14 @@ private void ValidateUXMetadata(string moduleName, string UXMetadataPath, Module
267
317
string UXMetadataContent = File . ReadAllText ( UXMetadataPath ) ;
268
318
string resourceType = Path . GetFileName ( Path . GetDirectoryName ( UXMetadataPath ) ) ;
269
319
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 ) ;
272
328
}
273
329
274
330
@@ -290,15 +346,15 @@ public AnalysisReport GetAnalysisReport()
290
346
public static class LogExtensions
291
347
{
292
348
public static void LogUXMetadataIssue (
293
- this ReportLogger < UXMetadataIssue > issueLogger , string module , string resourceType , string subResourceType , string command ,
349
+ this ReportLogger < UXMetadataIssue > issueLogger , IssueLoggerContext context ,
294
350
int severity , string description )
295
351
{
296
352
issueLogger . LogRecord ( new UXMetadataIssue
297
353
{
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 ,
302
358
Description = description ,
303
359
Severity = severity ,
304
360
} ) ;
0 commit comments